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

import {
  ShapeRect,
  MySprite, tweenChange,
  randomSortByArr,
  removeItemFromArr
} from './Unit';
import { res, resAudio } from './resources';

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

import TWEEN from '@tweenjs/tween.js';

import { Stage } from './Stage';
import { AudioAnimation } from './AudioAnimation';

import {
  Player
} from './Player';

import {
  Box
} from './Box';

import {
  Exercise,
  Answer
} from './Exercises';

import { Handclap } from './Handclap';

import { Petal } from './PetalUtil';

import {
  Buttion
} 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;

  boxRenderArr = [];

  endRenderArr = [];

  mapScale = 1;
  mapScaleX = 1;
  mapScaleY = 1;

  canvasLeft;
  canvasTop;

  saveKey = 'DF-L211';

  canTouch = false;

  canPlay = false;

  gameover = false;

  /**舞台 */
  stage;

  audioTool;

  rightBox;

  midBox;

  leftBox;

  exerciseArr = [];

  tempExerciseArr = [];

  curExercise;

  curIndex = 0;

  restartBtn;

  player;

  lemonPosArr = [];

  lemonArr = [];

  playerPosArr = [];

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


  ngOnInit() {

    this.data = {};

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

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

      // 初始化 各事件监听
      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) => {
        resolve();
      };
      audio.onerror = () => {
        reject();
      };
      audio.src = url;
      audio.load();
    });
  }


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





  checkClickTarget(target) {

    const rect = target.getBoundingBox();

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

  getWorlRect(target) {

    let rect = target.getBoundingBox();

    if (target.parent) {

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

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





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

    const audioObj = this.audioObj;

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

    this.rawAudios.set(key, url);

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

    audioObj[key] = audio;

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

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






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





  /**
   * 添加默认数据 便于无数据时的展示
   */
  initDefaultData() {
    if (!this.data.exercisesArr) {
      this.data.exercisesArr = [];
    }

  }


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

    for (let i = 0; i < this.data.exercisesArr.length; ++i) {
      let exercises = this.data.exercisesArr[i];

      if (!exercises.answers) {
        continue;
      }

      for (let j = 0; j < exercises.answers.length; ++j) {

        let answer = exercises.answers[j];

        if (answer.contentType == 'P' && answer.pic_url) {
          this.addUrlToImages(answer.pic_url);
        }

      }
    }

  }

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


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

    // 音效
    this.addUrlToAudioObj('wrong', this.rawAudios.get('wrong'));
    // 音效
    this.addUrlToAudioObj('right', this.rawAudios.get('right'));

    this.addUrlToAudioObj('handclap', this.rawAudios.get('handclap'));

    this.addUrlToAudioObj('win', this.rawAudios.get('win'));

    this.addUrlToAudioObj('clickBtn', this.rawAudios.get('clickBtn'));

    this.addUrlToAudioObj('pull', this.rawAudios.get('pull'));

    this.addUrlToAudioObj('lemon', this.rawAudios.get('lemon'));

    this.addUrlToAudioObj('sweat', this.rawAudios.get('sweat'));

    for (let i = 0; i < this.data.exercisesArr.length; ++i) {
      let exercises = this.data.exercisesArr[i];

      if (exercises.audio_url) {
        this.addUrlToAudioObj(exercises.audio_url);
      }
    }
  }



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

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

    this.mapScaleX = sx;
    this.mapScaleY = sy;


    this.renderArr = [];

    this.boxRenderArr = [];

    this.endRenderArr = [];

    this.exerciseArr = [];

    this.tempExerciseArr = [];

    this.canTouch = false;

    this.canPlay = false;

    this.gameover = false;

    this.curExercise = null;

    this.curIndex = 0;

    this.lemonPosArr = [];

    this.playerPosArr = [];

    this.lemonArr = [];

    const getLemonY = (x) => {
      //斜率
      let k = 0.195;

      let b = - 385 * this.mapScale;

      let y = k * x + b;

      return -y;

    }

    let startX = 427 * this.mapScale;

    let interval = 134 * this.mapScale;

    for (let i = 0; i < 6; ++i) {
      let y = getLemonY(startX);

      this.lemonPosArr.push({ x: startX, y: y });

      startX += interval;
    }

    startX = 170 * this.mapScale;

    for (let i = 0; i < 6; ++i) {
      let y = this.getPlayerY(startX);

      this.playerPosArr.push({ x: startX, y: y });

      startX += interval;
    }
  }

  getPlayerY = (x) => {
    //斜率
    let k = 0.195;

    let b = - 300 * this.mapScale;

    let y = k * x + b;

    return -y;

  }


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

    this.initBg();

    this.initPic();

    this.initPlayer();

    this.initBottomPart();

    this.initExercises();

    this.initGame();
  }

  initBg() {

    let width = 1280 * this.mapScale;

    let height = 720 * this.mapScale;

    let col = Math.ceil(this.canvasWidth / width);

    let row = Math.ceil(this.canvasHeight / height);

    const getBg = () => {
      const bg = new MySprite();
      bg.init(this.images.get('bg_1'));
      bg.setScaleXY(this.mapScale);
      this.renderArr.push(bg);
      return bg;
    };

    let startY = 0;

    let startX = 0;

    for (let i = 0; i < row; ++i) {

      startY = startY + height * i;

      for (let j = 0; j < col; ++j) {

        startX = startX + width * j;

        let bg = getBg();

        bg.x = startX + bg.getBoundingBox().width / 2;

        bg.y = startY + bg.getBoundingBox().height / 2;

      }

    }

  }

  initBottomPart() {
    const initBox = (name) => {

      const box = new Box('right', this.images);
      box.setScaleXY(this.mapScale);
      box.init();
      box.boxBg.y = this.canvasHeight - box.boxBg.height + 20 * this.mapScale;
      box.rightAudio = this.audioObj['right'];
      box.wrongAudio = this.audioObj['wrong'];
      this.boxRenderArr.push(box.boxBg);
      return box;
    }

    const rightBox = initBox('right');
    rightBox.boxBg.x = 869 * this.mapScale;
    this.rightBox = rightBox;

    const midBox = initBox('mid');
    midBox.boxBg.x = 506 * this.mapScale;
    this.midBox = midBox;

    const leftBox = initBox('left');
    leftBox.boxBg.x = 133 * this.mapScale;
    this.leftBox = leftBox;
  }

  initPic() {

    const stage = new Stage(this.images);
    stage.setScaleXY(this.mapScale);
    stage.init();
    this.stage = stage;
    stage.bg.x = (this.canvasWidth - stage.bg.width) / 2;
    stage.bg.y = this.canvasHeight - stage.bg.height;

    this.renderArr.push(stage.bg);

    const audio = new AudioAnimation(this.images);
    audio.setXY(849 * this.mapScale, 0);
    audio.initPic(stage.bg, this.mapScale, this.mapScale, true);
    this.audioTool = audio;

    const restartBtn = new Buttion(this.images);
    restartBtn.setScaleXY(this.mapScale);
    restartBtn.init('btn_restart_up', 'btn_restart_down');
    restartBtn.audio = this.audioObj['clickBtn'];
    this.restartBtn = restartBtn;
  }

  initPlayer() {
    const player = new Player(this.images);
    player.setScaleXY(this.mapScale);
    player.sweatAudio = this.audioObj['sweat'];
    player.pullAudio = this.audioObj['pull'];
    player.init();
    player.bg.x = 101 * this.mapScale;
    player.bg.y = 280 * this.mapScale;
    player.initX = player.bg.x;
    player.initY = player.bg.y;
    this.player = player;

    this.stage.stage.addChild(player.bg);
  }

  initLemon() {

    let lemonNum = 6;

    if (lemonNum > this.tempExerciseArr.length) {
      lemonNum = this.tempExerciseArr.length;
    }

    for (let i = 0; i < lemonNum; ++i) {
      const lemon = new MySprite();
      lemon.init(this.images.get('lemon'), 0, 0);
      lemon.setScaleXY(this.mapScale);
      lemon.x = this.lemonPosArr[i].x;
      lemon.y = this.lemonPosArr[i].y;
      this.stage.stage.addChild(lemon);
      this.lemonArr.push(lemon);
    }
  }


  initExercises() {

    for (let i = 0; i < this.data.exercisesArr.length; ++i) {
      let exercises = this.data.exercisesArr[i];

      let exercise = new Exercise(exercises);
      exercise.listeningAudio = this.audioObj[exercises.audio_url];

      for (let j = 0; j < exercises.answers.length; ++j) {
        let answer = new Answer(this.images, exercises.answers[j]);
        answer.setScaleXY(this.mapScale);
        answer.init();
        exercise.addAnswer(answer);

        answer.bg.x = 0;
        answer.bg.y = this.canvasHeight + answer.bg.height;

      }

      this.exerciseArr.push(exercise);
    }
  }

  initGame() {

    this.tempExerciseArr = randomSortByArr(this.exerciseArr);

    this.lemonArr = [];

    this.initLemon();

    this.curIndex = 0;

    this.gameover = false;

    this.showNextExercise();
  }

  showNextExercise() {
    let exercise = this.tempExerciseArr.shift();

    //如果存在，则加载；不存在，则表示结束
    if (exercise) {

      this.curIndex += 1;

      this.stage.setPromptText(this.curIndex + "/" + this.exerciseArr.length);

      let answers = exercise.answers;

      this.curExercise = exercise;

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

        if (i == 0) {
          this.leftBox.showBox(answers[i], () => {
            this.canTouch = true;
            this.canPlay = true;
          });
        }
        else if (i == 1) {
          this.midBox.showBox(answers[i]);
        }

        if (i == 2) {
          this.rightBox.showBox(answers[i]);
        }
      }

      if (answers.length < 3) {
        let lastAnswer = this.rightBox.answer;
        this.rightBox.hide(lastAnswer);
      }
    }
    else {
      this.curExercise = null;
      this.gameover = true;

      this.showGameOver();
    }
  }


  mapDown(event) {

    if (!this.canTouch) {
      return;
    }

    if (this.checkClickTarget(this.audioTool.bg)) {

      if (this.audioTool.playing) {
        this.audioTool.pauseAudio(true);
      }
      else {
        if (this.curExercise) {
          let audio = this.curExercise.listeningAudio;

          this.audioTool.playAudio(audio, true);
        }

      }
      return;
    }

    if (this.checkClickTarget(this.leftBox.boxBg)) {
      this.clickBox(this.leftBox);
      return;
    }

    if (this.checkClickTarget(this.midBox.boxBg)) {
      this.clickBox(this.midBox);
      return;
    }

    if (this.checkClickTarget(this.rightBox.boxBg)) {
      this.clickBox(this.rightBox);
      return;
    }

    if (this.checkClickTarget(this.restartBtn.bg)) {
      this.restartBtn.click(() => {

        this.player.reset();

        //卸车
        this.player.unloading();

        this.restartBtn.hide(0.3, () => {

          removeItemFromArr(this.endRenderArr, this.restartBtn.bg);

          this.curIndex = 0;

          this.stage.setPromptText(this.curIndex + "/" + this.exerciseArr.length);

          this.stage.openDoor(() => {
            this.initGame();
          });
        });

      });
      return;
    }
  }

  mapMove(event) {

  }

  mapUp(event) {

  }



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

    this.updateArr(this.endRenderArr);
  }


  clickBox(box) {

    this.canTouch = false;

    box.click(() => {
     
      let answers = this.curExercise.answers;

      let selectAll = true;

      for (let i = 0; i < answers.length; ++i) {
        let answer = answers[i];

        if (answer.right) {
          if (!answer.selected) {
            selectAll = false;
            break;
          }
        }
      };

      //全部选中，加载下一道题
      if (selectAll) {

        //获取小车拉坐坐标
        let index = (this.curIndex - 1) % 6;

        let pos = this.playerPosArr[index];

        this.player.pullCar(pos.x, pos.y, () => {

          let lemon = this.lemonArr[this.curIndex - 1];

          //从舞台上移除lemon
          this.stage.stage.removeChild(lemon);

          //装车
          this.player.loading(lemon);

          //如果到达坡顶，练习题未结束，则小车从坡顶划出，从坡底划入
        if(this.curIndex == 6 && this.tempExerciseArr.length > 0){
          let toX = this.stage.stage.width + this.player.bg.width;
          let toY = this.getPlayerY(toX);

          this.player.pullCar(toX, toY, ()=>{
            let sX = -this.player.bg.width;
            let sY = this.getPlayerY(sX);

            this.player.bg.x = sX;
            this.player.bg.y = sY;

            this.initLemon();

            this.player.pullCar(this.player.initX, this.player.initY, ()=>{
              //显示下一个练习题
              this.showNextExercise();
            }, 1);
          }, 1.5);
        }
        else{
          //显示下一个练习题
          this.showNextExercise();
        }
         
        });

      }
      else {
        this.canTouch = true;
      }
    }, () => {
      this.canTouch = true;
      this.player.flowSweat();
    });
  }

  showGameOver() {

    this.canTouch = false;

    this.playAudio('win');

    this.audioTool.pauseAudio(true);
    this.audioTool.curAudio = null;

    const showCelebrate = () => {


      const showPetal = () => {
        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);

        //播放鼓掌
        const handClap = new Handclap();

        handClap.init(this.images.get('left_hand'), this.images.get('right_hand'), this.mapScale, this.mapScale);

        handClap.bg.x = (this.canvasWidth - handClap.getWidth()) / 2;
        handClap.bg.y = (this.canvasHeight - handClap.getHight()) / 2;

        handClap.audio = this.audioObj['handclap'];

        this.endRenderArr.push(handClap.bg);

        handClap.play(() => {

          removeItemFromArr(this.endRenderArr, handClap.bg);

          //隐藏cover
          tweenChange(cover, { alpha: 0 }, 0.3, () => {

            removeItemFromArr(this.endRenderArr, cover);
            this.restartBtn.resetStatus();
            this.endRenderArr.push(this.restartBtn.bg);

            this.restartBtn.bg.x = (this.canvasWidth - this.restartBtn.bg.width) / 2;
            this.restartBtn.bg.y = this.canvasHeight - 185 * this.mapScale - this.restartBtn.bg.height;

            this.canTouch = true;
          })
        });

        setTimeout(() => {
          //结束花瓣显示
          petal.stop(() => {


          });
        }, 2000);
      }

      let cover = new ShapeRect();
      cover.setSize(this.canvasWidth, this.canvasHeight);
      cover.fillColor = "#696969";
      cover.alpha = 0.7;
      cover.x = 0;
      cover.y = 0;
      this.endRenderArr.push(cover);

      showPetal();
    }

    //关闭大门
    this.stage.closeDoor(() => {

      //将答案滑出屏幕
      this.leftBox.moveOut(() => {
        showCelebrate();
      });

      this.midBox.moveOut();

      this.rightBox.moveOut();
    });

  }
}
