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

import {
  Label,
  MySprite, RichText, ShapeRect,
  Text
} from './Unit';
import { res, resAudio } from './resources';

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

import TWEEN from '@tweenjs/tween.js';
import { getDefaultTile } from '../bean/TitleBean';
import { Listening } from './Listening';
import { Title } from './Title';
import { Index } from './Index';
import { Scrollbar } from './ScrollBar';
import { defaultVal } from './defaultValue';




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

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

  // 数据
  data;

  ctx;

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

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

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


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

  images = new Map();

  animationId: any;
  winResizeEventStream = new Subject();

  audioObj = {};

  renderArr;
  mapScale = 1;

  canvasLeft;
  canvasTop;

  saveKey = 'YM5-17';


  canTouch = true;

  /**标题*/
  title;

  listening;

  curAudio;

  indexArr = [];

  scrollbar: Scrollbar;

  sentenceArr = [];

  curRowIndex = 0;

  sentenceBg: ShapeRect;

  posArr = [];

  indexLabelArr = [];

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


  ngOnInit() {

    this.data = {};

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

      if (data && typeof data == 'object') {
        this.data = data;
      }
      else{
        this.data = defaultVal;
      }
      //console.log('data:', JSON.stringify(data));

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

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

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

    }, this.saveKey);

  }

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


  cleanAudio() {
    if (this.audioObj) {
      for (const key in this.audioObj) {
        this.audioObj[key].pause();
      }
    }
  }


  load() {

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


  init() {

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

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


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

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






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

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







  initListener() {

    this.winResizeEventStream
      .pipe(debounceTime(500))
      .subscribe(data => {
        this.renderAfterResize();
      });


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

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


    let firstTouch = true;

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

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


    const element = this.canvas.nativeElement;

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

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

    addMouseListener();
    addTouchListener();
  }


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

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

      if (now) {
        audio.pause();
        audio.currentTime = 0;
      }

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

      this.curAudio = audio;

      return audio;
    }
  }

  pauseAudio(audio){
    if (audio) {
      if (this.listening && audio == this.listening.audio) {
        this.listening.pauseAudio();
      }
      else {
        audio.pause();
      }
    }
  }



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

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

      pr.push(p);
    });

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

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

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

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

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


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





  checkClickTarget(target) {

    const rect = target.getBoundingBox();

    if (this.checkPointInRect(this.mx, this.my, rect)) {
      return true;
    }
    return false;
  }

  getWorlRect(target) {

    let rect = target.getBoundingBox();

    if (target.parent) {

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

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





  addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null) {

    const audioObj = this.audioObj;

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

    this.rawAudios.set(key, url);

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

    audioObj[key] = audio;

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

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






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





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

    if (!this.data.title) {
      this.data.title = getDefaultTile();
    }
  }


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

    if (this.data.bgItem && this.data.bgItem.url) {
      this.addUrlToImages(this.data.bgItem.url);
    }

  }

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

    // 音效
    this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);

    //标题发音
    if (this.data.title.audioUrl) {
      this.addUrlToAudioObj('titleAudio', this.data.title.audioUrl);
    }


    //听力材料
    if (this.data.listenAudioUrl) {
      this.addUrlToAudioObj('listenAudio', this.data.listenAudioUrl);
    }

    if (this.data.sentenceArr) {

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

        let sentence = this.data.sentenceArr[i];

        if (sentence.audioUrl) {
          this.addUrlToAudioObj(sentence.audioUrl);
        }
      }

    }
  }



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

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

    this.renderArr = [];

    this.indexArr = [];

    this.sentenceArr = [];

    this.curRowIndex = 0;

    this.posArr = [];

    this.indexLabelArr = [];
  }



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

    this.initTitle();

    this.initBottomPart();

    this.initPic();

    this.initSentence();
  }

  initBottomPart() {

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

    const listening = new Listening(this.audioObj['listenAudio'], this.images);
    listening.init(this.mapScale, this.mapScale);

    listening.bg.x = this.canvasWidth - listening.bg.width - 28 * this.mapScale;
    listening.bg.y = this.canvasHeight - listening.bg.height - 6.5 * this.mapScale;
    this.listening = listening;

    this.renderArr.push(listening.bg);
  }

  initTitle() {
    const title = new Title(this.data.title, this.images);

    title.init(this.mapScale, this.mapScale);
    title.audio = this.audioObj['titleAudio'];
    this.title = title;

    title.titleBg.y = 0;
    title.titleBg.x = 0;

    this.renderArr.push(title.titleBg);
  }

  initPic() {

    if (!this.data.bgItem.url) {
      return;
    }

    let width = 371 * this.mapScale;
    let height = 539 * this.mapScale;

    const bg = new ShapeRect();
    bg.setSize(width, height);
    bg.alpha = 0;
    this.renderArr.push(bg);
    bg.x = this.canvasWidth / 2 + 194 * this.mapScale;
    bg.y = (this.canvasHeight - height) / 2;

    const pic = new MySprite();
    pic.init(this.images.get(this.data.bgItem.url));

    const rate1 = width / pic.width;
    const rate2 = height / pic.height;
    const rate = Math.min(rate1, rate2);
    pic.setScaleXY(rate);
    pic.x = width / 2;
    pic.y = height / 2;
    bg.addChild(pic);

    const getIndex = (item) => {

      let index = new Index(item, this.images);
      index.init(this.mapScale, this.mapScale);

      return index;
    }

    let hotZoneItemArr = this.data.hotZoneItemArr;

    for (let i = 0; i < hotZoneItemArr.length; ++i) {
      let hotZoneItem = hotZoneItemArr[i];
      let rect = hotZoneItem.rect;
      let index = getIndex(hotZoneItem);
      index.bg.x = (hotZoneItem.rect.x + rect.width / 2) / hotZoneItem.mapScale * this.mapScale;
      index.bg.y = (hotZoneItem.rect.y + rect.height / 2) / hotZoneItem.mapScale * this.mapScale;
      this.indexArr.push(index);
      index.data = hotZoneItem;
      bg.addChild(index.bg);
    }
  }

  private getIndex(index){
    
    const indexLabel = new Label();

    indexLabel.textAlign = 'right';
    indexLabel.text = index + ".";
    indexLabel.fontColor = "#000000";
    indexLabel.fontSize = 36;
    indexLabel.fontName = "GOTHICB";
    indexLabel.setScaleXY(this.mapScale);
    indexLabel.refreshSize();

    return indexLabel;
  }

  startY = 0;
  initY = 0;
  initSentence() {

    let width = 790 * this.mapScale;

    let startY = this.canvasHeight / 2 - 265 * this.mapScale;

    if (this.data.titleVal) {
      const title = new Label();
      title.text = this.data.titleVal;
      title.textAlign = "center";
      title.fontSize = 48;
      title.fontColor = "#763c92";
      title.setScaleXY(this.mapScale);
      title.fontName = "GOTHICB";
      title.refreshSize();
      title.x = width / 2;
      title.y = this.canvasHeight / 2 - 285 * this.mapScale + title.getBoundingBox().height / 2;

      this.renderArr.push(title);

      startY = this.canvasHeight / 2 - 204 * this.mapScale;
    }

    const getTextArr = (index, sentence) => {
      let textArr = [];

      for (let i = 0; i < sentence.letterArr.length; ++i) {
        let text = new Text();
        text.val = sentence.letterArr[i].val;
        text.fontColor = sentence.letterArr[i].fontColor;
        textArr.push(text);
      }
      return textArr;
    }

    this.startY = startY;
    this.initY = startY;
    let viewHight = Math.floor( 560 * this.mapScale);

    const bg = new ShapeRect();
    bg.setSize(680 * this.mapScale, viewHight);
    this.renderArr.push(bg);
    bg.x = 70 * this.mapScale;
    bg.y = startY;
    bg.alpha = 0;
    this.sentenceBg = bg;

    let y = 0;

    let dH = Math.floor(24 * this.mapScale) ;

    let totalHeight = 0;

    for (let i = 0; i < this.data.sentenceArr.length; ++i) {
      let sentence = this.data.sentenceArr[i];
      if(!sentence.renderTo){
        continue;
      }
      this.posArr.push(y);     
      const text = new RichText();
      text.fontColor = "#000000";
      text.fontSize = 36;
      text.fontName = "GOTHICB";
      text.textArr = getTextArr(i + 1, sentence);
      text.disH = 20;
      text.maxWidth = 680;
      text.setScaleXY(this.mapScale);
      text.refreshSize();
      text.y = y + text.getBoundingBox().height / 2;
      text.x = 0;
      text.data = sentence;
      text.alpha = 0;
      text.visible = false;
      this.sentenceBg.addChild(text);

      this.sentenceArr.push(text);

      const indexLabel = this.getIndex(i + 1);
      indexLabel.x = -10 * this.mapScale;
      indexLabel.y = y + indexLabel.getBoundingBox().height - 20 * this.mapScale / 2;
      indexLabel.alpha = 0;
      indexLabel.visible = false;
      this.sentenceBg.addChild(indexLabel);
      this.indexLabelArr.push(indexLabel);

      y += text.getBoundingBox().height + dH;

      totalHeight += text.getBoundingBox().height + dH;

      
    }

    let scrollbar = new Scrollbar();
    scrollbar.init(this.mapScale, this.mapScale, viewHight, Math.floor(totalHeight - dH), 14);
    scrollbar.showScrollbar();
    scrollbar.hide();
    scrollbar.bg.x = this.canvasWidth / 2 + 161 * this.mapScale;
    scrollbar.bg.y = this.canvasHeight / 2 - 231 * this.mapScale;
    this.renderArr.push(scrollbar.bg);
    this.scrollbar = scrollbar;
  }


  mapDown(event) {

    if (!this.canTouch) {
      return;
    }


    if (this.listening && this.checkClickTarget(this.listening.bg)) {

      let tempAudio = this.curAudio;

      if(tempAudio){
        tempAudio.pause();
      }

      this.curAudio = this.listening.play(() => {
        this.listening.reset();
      });

      return;
    }

    if (this.checkClickTarget(this.title.titleBg)) {
      this.pauseAudio(this.curAudio);
      let audio = this.title.playAudio();
      if(audio){
        this.curAudio = audio;
      }
      return;
    }

    let clicked = false;

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

      if (this.checkClickTarget(index.bg)) {
        clicked = true;
        this.clickIndex(index);
        break;
      }
    }

    if (clicked) {
      return;
    }

    clicked = false;


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

      let sentenceSpr = this.sentenceArr[i];

      if (this.checkClickTarget(sentenceSpr) && sentenceSpr.visible && sentenceSpr.alpha == 1) {
        clicked = true;
        this.playAudio(sentenceSpr.data.audioUrl, true);
        break;
      }
    }

    if (clicked) {
      return;
    }


    if (this.scrollbar.shown && this.checkClickTarget(this.scrollbar.scrollbar)) {
      this.scrollbar.touchScrollbar(this.my);
      return;
    }

  }

  mapMove(event) {

    if (this.scrollbar.touched) {

      let dis = this.my - this.scrollbar.touchY;

      this.scrollbar.drag(dis, (dis) => {     
        this.sentenceBg.y = this.startY - dis;
        this.setShowRow();
      });
    }

  }

  mapUp(event) {
    if (this.scrollbar.touched) {
      this.scrollbar.touched = false;
      this.startY = this.sentenceBg.y;
      return;
    }

  }



  update() {

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



    this.updateArr(this.renderArr);


  }


  clickIndex(indexItem) {

    const getSentence = (key) => {

      let item;

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

        let sentence = this.data.sentenceArr[i];

        if (sentence.renderTo == key) {
          item = sentence;
          break;
        }

      }

      return item;
    }

    let sentence = indexItem.sentence;

    if (!sentence) {
      sentence = getSentence(indexItem.data.key);
      indexItem.sentence = sentence;
    }

    let spr;

    let index = -1;

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

      let sentenceSpr = this.sentenceArr[i];
  
      if(sentenceSpr.data.renderTo == sentence.renderTo){
        spr = sentenceSpr;
        index = i;
        break;
      }

    }

    this.showSentence(spr, index);
  }

  private showSentence(sentence, index){
    if(!sentence){
      return;
    }

    let y = sentence.getBoundingBox().y;

    let h = Math.floor(sentence.getBoundingBox().height);

    let offsetY = Math.floor(y - h / 2);

    sentence.alpha = 1;
    this.indexLabelArr[index].alpha = 1;


    let dH = 24 * this.mapScale;
    console.log(offsetY, Math.floor(this.initY), Math.floor(this.initY + this.sentenceBg.height))

    let maxY = Math.floor(this.initY + this.sentenceBg.height);

    let minY = Math.floor(this.initY);

    //如果句子不在可视窗口，则进行定位滚动
    if(offsetY < minY || offsetY >= maxY || (offsetY < maxY && offsetY + h >= maxY)){

      let dis = 0;

      if(offsetY < Math.floor(this.initY)){
        dis = Math.floor(this.initY) - offsetY;
      }
      else if(offsetY >= maxY || (offsetY < maxY && offsetY + h >= maxY)){
        dis = -(offsetY - maxY+ h + dH);
      }

      this.sentenceBg.y += dis;

      this.scrollbar.setOffset(-dis);

      this.scrollbar.show();

      this.startY = this.sentenceBg.y;
    } 

    this.setShowRow();
  }

  private setShowRow() {

    let len = this.sentenceArr.length;
   
    for(let i = 0; i < len; ++ i){

      let sentence = this.sentenceArr[i];

      let y = sentence.getBoundingBox().y;

      let h = sentence.getBoundingBox().height;

      let offsetY = Math.floor(y - h / 2);
     
      if(offsetY < Math.floor(this.initY - 5 * this.mapScale) || (offsetY + Math.floor(h)) > Math.floor(this.initY + this.sentenceBg.height)){
        sentence.visible = false;
        this.indexLabelArr[i].visible = false;
      }
      else{
        sentence.visible = true;
        this.indexLabelArr[i].visible = true;
      }
      
    }


    if(len > 0){
      //如果到达顶部，则默认显示第一行
      if(this.scrollbar.isTop()){
        this.sentenceArr[0].visible = true;
        this.indexLabelArr[0].visible = true;
      }
      else if(this.scrollbar.isBottom()){
        this.sentenceArr[len - 1].visible = true;
        this.indexLabelArr[len - 1].visible = true;
      }
    }

    
  }

}
