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

import {
  Label,
  MySprite, tweenChange, ShapeRect, removeItemFromArr

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

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

import TWEEN from '@tweenjs/tween.js';
import { Backagroud } from './Background';
import { Course, Sheep, Sheepfold } from '../form/Course';
import { Stage, SheepfoldItem, SheepItem } from './Stage';
import { Timer } from './Timer';
import { Petal } from './PetalUtil'
import { Button } from './Button';



@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;
  endRenderArr = [];

  canvasLeft;
  canvasTop;

  saveKey = 'DF-L209';

  stage: Stage;

  canTouch = true;

  lastSheepfold: SheepfoldItem;

  oldPos;

  curSheepItem: SheepItem;

  timer: Timer;

  canPlay = false;

  btnRestart: Button;

  btnShowX = 0;

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


  ngOnInit() {

    this.data = new Course();

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

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

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

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

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

    }, this.saveKey);

  }

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


  load() {

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


  init() {

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

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


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

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






  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) {
      if (now) {
        audio.pause();
        audio.currentTime = 0;
      }

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



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

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

      pr.push(p);
    });

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

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

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

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

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


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





  checkClickTarget(target) {

    const rect = target.getBoundingBox();

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

  getWorlRect(target) {

    let rect = target.getBoundingBox();

    if (target.parent) {

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

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





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

    let sheepfoldId = 'test_';

    if (this.data.sheepfoldArr.length === 0 && this.data.sheepArr.length === 0) {

      for (let i = 0; i < 2; ++i) {
        let sheepfold = new Sheepfold();
        sheepfold.word = 'he';
        sheepfold.id = sheepfoldId + i;

        this.data.sheepfoldArr.push(sheepfold);

        for (let j = 0; j < 3; ++j) {
          let sheep = new Sheep();
          sheep.word = 'Hello';
          sheep.sheepfoldId = sheepfold.id;
          this.data.sheepArr.push(sheep);
        }
      }
    }
  }


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

    for (let i = 0; i < this.data.sheepArr.length; ++i) {
      if (this.data.sheepArr[i].picUrl) {
        this.addUrlToImages(this.data.sheepArr[i].picUrl);
      }
    }
    for (let i = 0; i < this.data.sheepfoldArr.length; ++i) {
      if (this.data.sheepfoldArr[i].picUrl) {
        this.addUrlToImages(this.data.sheepfoldArr[i].picUrl);
      }
    }
  }

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

    // 音效
    this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
    this.addUrlToAudioObj('right', this.rawAudios.get('right'));
    this.addUrlToAudioObj('wrong', this.rawAudios.get('wrong'));
    this.addUrlToAudioObj('win', this.rawAudios.get('win'));
    this.addUrlToAudioObj('restart', this.rawAudios.get('restart'));

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

      if (this.data.sheepfoldArr[i].audioUrl) {
        this.addUrlToAudioObj(this.data.sheepfoldArr[i].id, this.data.sheepfoldArr[i].audioUrl);
      }
    }

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

      if (this.data.sheepArr[i].audioUrl) {
        this.addUrlToAudioObj(this.data.sheepArr[i].audioUrl);
      }
    }
  }



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

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

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


    this.renderArr = [];

    this.endRenderArr = [];

    this.canPlay = false;

  }

  getPic(imageName, anchorX: number = 0.5, anchorY: number = 0.5) {
    const pic = new MySprite();
    pic.init(this.images.get(imageName), anchorX, anchorY);
    pic.setScaleXY(this.mapScale);
    return pic;
  }

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

    this.initBg();

    this.initStage();

    this.initButton();

    this.initGame();
  }

  initBg() {

    const sx = this.canvasWidth / this.canvasBaseW;
    const sy = this.canvasHeight / this.canvasBaseH;

    const bg = new Backagroud(this.images, this.audioObj, sx, sy);
    bg.x = this.canvasWidth / 2;
    bg.y = this.canvasHeight / 2;
    bg.init('bg');

    this.renderArr.push(bg.pic);


    const leftLeaf = this.getPic('left_leaf', 0, 0);
    leftLeaf.x = 0;
    leftLeaf.y = 0;
    this.renderArr.push(leftLeaf);

    const rightLeaf = this.getPic('right_leaf', 1, 0);
    rightLeaf.x = this.canvasWidth;
    rightLeaf.y = 0;
    this.renderArr.push(rightLeaf);

    const land = this.getPic('land', 0.5, 0);
    land.x = this.canvasWidth / 2;
    land.y = this.canvasHeight - 550 * this.mapScale;
    this.renderArr.push(land);
  }

  initStage() {
    const stage = new Stage(this.images, this.audioObj, this.mapScale, this.mapScale);
    stage.init(this.data);
    stage.x = (this.canvasWidth - stage.width) / 2;
    stage.y = this.canvasHeight - 550 * this.mapScale;
    this.renderArr.push(stage.bg);
    this.stage = stage;

    const timer = new Timer(this.images, this.audioObj, this.mapScale, this.mapScale);
    timer.init();
    this.timer = timer;
    timer.x = (this.canvasWidth - timer.width) / 2;
    timer.y = -(timer.height - 60 * this.mapScale);
    timer.initX = timer.x;
    timer.initY = timer.y;
  }

  initButton() {

    this.btnShowX = this.canvasWidth - 20 * this.mapScale - 331 * this.mapScale / 2;

    const btnRestart = new Button(this.images, this.audioObj, this.mapScale, this.mapScale);
    this.btnRestart = btnRestart;
    btnRestart.x = this.canvasWidth + 331 * this.mapScale;
    btnRestart.y = this.canvasHeight - 50 * this.mapScale - 112 * this.mapScale / 2;
    btnRestart.outX = btnRestart.x;
    btnRestart.outY = btnRestart.y;
    btnRestart.init('restart');
  }


  mapDown(event) {

    this.oldPos = { x: this.mx, y: this.my };

    if (!this.canTouch) {
      return;
    }


    if (this.canPlay) {
      if (this.lastSheepfold) {
        this.lastSheepfold.stopAudio();
      }

      let sheepArr = this.stage.sheepArr;

      for (let i = 0; i < sheepArr.length; ++i) {
        let sheep = sheepArr[i];
        sheep.hideLight();
        sheep.stopAudio();
      }
      for (let i = 0; i < sheepArr.length; ++i) {
        let sheep = sheepArr[i];
        if (this.checkClickTarget(sheep.bg) && !sheep.answered) {

          this.timer.start();
          this.curSheepItem = sheep;
          sheep.showLight();
          sheep.playAudio();
          return;
        }
      }

      let sheepfoldArr = this.stage.sheepfoldArr;

      for (let i = 0; i < sheepfoldArr.length; ++i) {
        let sheepfold = sheepfoldArr[i];

        if (this.checkClickTarget(sheepfold.bubbleBg)) {
          this.lastSheepfold = sheepfold;
          sheepfold.click();
          return;
        }
      }


    }

    if (this.checkClickTarget(this.btnRestart.btnPic)) {

      this.btnRestart.click(() => {

        this.initGame();

      });

      return;
    }
  }

  mapMove(event) {

    if (!this.curSheepItem) {
      return;
    }

    let rect = this.stage.bg.getBoundingBox();

    if (this.checkPointInRect(this.mx, this.my, rect)) {
      const addX = this.mx - this.oldPos.x;
      const addY = this.my - this.oldPos.y;
      this.curSheepItem.x += addX;
      this.curSheepItem.y += addY;
    }

    this.oldPos = { x: this.mx, y: this.my };
  }

  mapUp(event) {


    if (this.curSheepItem) {

      let sheepItem = this.curSheepItem;

      let right = false;

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

        let sheepfoldItem = this.stage.sheepfoldArr[i];


        if (this.checkPointInRect(this.mx, this.my, sheepfoldItem.bg.getBoundingBox())) {

          this.canTouch = false;

          right = sheepItem.checkRight(sheepfoldItem);

          if (right) {

            sheepItem.stopAudio();

            sheepItem.hideLight();

            this.stage.sheepBg.removeChild(sheepItem.bg);

            sheepItem.into(sheepfoldItem, () => {

              let petal = new Petal(this.endRenderArr, this.images, this.ctx);

              let rect = sheepItem.bg.getBoundingBox();
              //显示花瓣
              petal.showPetal({ x: rect.x - 40 * this.mapScale, y: rect.y - 40 * this.mapScale, width: rect.height + 60 * this.mapScale, height: rect.height + 60 * this.mapScale }, this.mapScale, 200, 8);

              setTimeout(() => {
                petal.stop(() => {

                });
              }, 2000);
              this.canTouch = true;
            });

            this.checkWin();
          }
          else {
            this.playAudio('wrong', true);

          }
          break;
        }
      }

      if (!right) {
        tweenChange(sheepItem.bg, { x: sheepItem.initX, y: sheepItem.initY }, 0.3, () => {
          this.canTouch = true;
          sheepItem.x = sheepItem.initX;
          sheepItem.y = sheepItem.initY;
        });
      }
    }

    this.curSheepItem = null;
  }



  update() {

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



    this.updateArr(this.renderArr);

    this.updateArr(this.endRenderArr);
  }

  initGame() {

    removeItemFromArr(this.endRenderArr, this.timer.bg);
    this.renderArr.push(this.timer.bg);
    this.endRenderArr = [];

    this.timer.reset(() => {
      this.timer.standby();
    });

    this.stage.randomSheep();

    this.canPlay = true;
  }

  checkWin() {

    let win = true;

    for (let i = 0; i < this.stage.sheepArr.length; ++i) {
      let sheep = this.stage.sheepArr[i];

      if (sheep.data.sheepfoldId && !sheep.answered) {
        win = false;
        break;
      }

    }

    if (win) {
      this.canPlay = false;

      this.timer.stop();

      setTimeout(() => {
        this.showEnd();
      }, 2000);

    }
  }

  showEnd() {

    const showRestartBtn = () => {

      this.btnRestart.x = this.btnRestart.outX;

      this.endRenderArr.push(this.btnRestart.btnPic);

      this.btnRestart.show(() => {
        tweenChange(this.btnRestart.btnPic, { x: this.btnShowX }, 0.3, () => {
          this.canTouch = true;
        });
      });
    };

    const mask = new ShapeRect();
    mask.setSize(this.canvasWidth, this.canvasHeight);
    mask.alpha = 0.5;
    mask.fillColor = "#000000";
    mask.x = 0;
    mask.y = 0;
    this.endRenderArr.push(mask);

    let petal = new Petal(this.endRenderArr, this.images, this.ctx);

    //显示花瓣
    petal.showPetal({ x: 0, y: 0, width: this.canvasWidth, height: this.canvasHeight }, this.mapScale, 100, 8);

    this.playAudio('win');

    setTimeout(() => {
      petal.stop(() => {


      });
    }, 2000);

    setTimeout(() => {
      showRestartBtn();

      removeItemFromArr(this.renderArr, this.timer.bg);

      this.endRenderArr.push(this.timer.bg);

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

      tweenChange(this.timer.bg, { y: 0 }, 0.3, () => {
        this.timer.y = this.timer.bg.y;

        this.timer.showStar();
      });

      tweenChange(this.timer.stopwatch, { y: this.timer.stopwatchY - 17 * this.mapScale }, 0.3);
      tweenChange(this.timer.timeLabel, { y: this.timer.timeY - 17 * this.mapScale }, 0.3);
    }, 1000);
  }

}
