import {
  MySprite,
  getMinScale,
  ShapeRect,
  tweenChange,
  randomSortByArr,
  Label,
  showPopParticle,
  moveItem,
  removeItemFromArr,
  rotateItem,
  ShapeRectNew,
  waterWave,
  ShapeCircle,
  MyAnimation
} from "./Unit";
import { matchesElement } from '@angular/animations/browser/src/render/shared';

export class Cartoon {

  // 系统缩放比例
  mapScale = 1;
  stageWidth;
  stageHeight;
  clientWidth;
  clientHeight;
  // 音乐 和 图片的缓冲区
  audio = new Map();
  images = new Map();
  imagesOriginSize = new Map();

  // 坐标原点 包含缩放
  originX = 0;
  originY = 0;

  setOrigin = (x, y) => {
    this.originX = x;
    this.originY = y;
  }
  getOrigin = () => {
    return {
      x: this.originX,
      y: this.originY
    }
  }

  // 相对坐标原点 包含缩放 用于添加孩子动画元素
  relativeOriginX = 0;
  relativeOriginY = 0;
  setRelativeOrigin = (x, y) => {
    this.relativeOriginX = x;
    this.relativeOriginY = y;
  }
  getRelativeOrigin = () => {
    return {
      x: this.relativeOriginX,
      y: this.relativeOriginY
    }
  }

  // 存放音乐和图片的地址
  audioObj = {}
  imageObj = {}
  _currentPlayAudio;
  // 添加音乐
  addAudio = (key, url) => {
    this.audioObj[key] = url
  };
  // 添加音乐
  addImage = (key, url) => {
    this.imageObj[key] = url
  };
  // 播放音乐
  _playingNow = []
  audioCallback = {}
  playAudio = function (key, now = false, callback = null, loop?, onplayingCallback?) {
    const audio = this.audio.get(key);
    if (audio) {
      if (now) {
        audio.pause();
        audio.currentTime = 0;
      }
      this.audioCallback[key] = {}
      if (callback) {
        this.audioCallback[key]["onended"] = () => {
          callback && callback()
        }
        audio.onended = () => {
          let index = this._playingNow.indexOf(audio)
          if (index != -1) {
            this._playingNow.splice(index, 1)
          }
          callback();
        };
      }
      if (onplayingCallback) {
        this.audioCallback[key]["onplaying"] = () => {
          onplayingCallback && onplayingCallback(audio)
        }
        audio.onplaying = () => {
          onplayingCallback && onplayingCallback(audio)
        }
      }
      audio.play();
      audio.callback = callback
      audio.loop = loop ? true : false
      this._playingNow.push(audio)
      this._currentPlayAudio = audio;
    }
    return audio
  }

  setAudioVolume(key, volume) {
    if (volume < 0 || volume > 1) {
      return
    }
    const audio = this.audio.get(key);
    audio.volume = volume
  }

  stopAllAudio(audioAll?) {
    if (!audioAll) {
      audioAll = this._playingNow
    }
    audioAll.forEach(audio => {
      if (audio) {
        const audio_URL = audio.src
        try {
          if (audio) {
            audio.onended && audio.onended()
            audio.pause && audio.pause();
            audio.currentTime = 0;
          } else {
            if (this.audioCallback[audio_URL] && this.audioCallback[audio_URL]["onended"]) {
              this.audioCallback[audio_URL]["onended"]()
            }
          }
        } catch (err) {
          console.log(err)
        }
      }
      else if (this._currentPlayAudio) {
        this._currentPlayAudio.pause();
        this._currentPlayAudio.currentTime = 0;
      }
    })
    this._playingNow = []
  }

  stopAudio(audio_URL?) {
    if (audio_URL) {
      const audio = this.audio.get(audio_URL);
      try {
        if (audio) {
          audio.onended && audio.onended()
          audio.pause();
          audio.currentTime = 0;
        } else {
          if (this.audioCallback[audio_URL] && this.audioCallback[audio_URL]["onended"]) {
            this.audioCallback[audio_URL]["onended"]()
          }
        }

      } catch (err) {
        console.log(err)
      }
    }
    else if (this._currentPlayAudio) {
      this._currentPlayAudio.pause();
      this._currentPlayAudio.currentTime = 0;
    }
  }

  pauseAudio(key) {
    const audio = this.audio.get(key);
    audio.pause()
  }


  // 异步加载图片 音频资源
  loadResources = () => {
    const pr = [];
    for (let key in this.imageObj) {
      const p = this.preloadImage(this.imageObj[key]).then((img: any) => {
        this.images.set(key, img);
        this.imagesOriginSize.set(key, { width: img.width, height: img.height });
      }).catch(err => console.log(key));
      pr.push(p);
    };
    for (let key in this.audioObj) {
      const a = this.preloadAudio(this.audioObj[key]).then((audio: any) => {
        this.audio.set(key, audio);
      }).catch(err => console.log(key));
      pr.push(a);
    };
    return Promise.all(pr);
  }

  // 预加载图片
  preloadImage = (url) => {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.src = url;
      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);
      };
      audio.onerror = () => {
        reject();
      };
      audio.src = url;
      audio.load();
    });
  }

  // 缓存页面元素
  cartoonElementsBuffer = {}
  createCartoonElement = (key, type) => {
    this.cartoonElementsBuffer[key] = {
      ref: null,
      get boundingBox(){
        return this.ref.getBoundingBox()
      }
    }
    this.cartoonElementsBuffer[key].id = key;
    switch (type) {
      case "MySprite": this.cartoonElementsBuffer[key].ref = new MySprite(); break;
      case "ShapeRect": this.cartoonElementsBuffer[key].ref = new ShapeRect(); break;
      case "ShapeRectNew": this.cartoonElementsBuffer[key].ref = new ShapeRectNew(); break;
      case "Label": this.cartoonElementsBuffer[key].ref = new Label(); break;
      case "waterWave": this.cartoonElementsBuffer[key].ref = new waterWave(); break;
      case "ShapeCircle": this.cartoonElementsBuffer[key].ref = new ShapeCircle(); break;
      default: this.cartoonElementsBuffer[key].ref = new MySprite(); break;
    }
    this.cartoonElementsBuffer[key].ref.id = key;
    return this.cartoonElementsBuffer[key]
  }

  // 创建图片
  createCartoonElementImage(id: string, image: string, width: number, height: number, initX: number, initY: number, withScale?: boolean) {
    let element = this.createCartoonElement(id, "MySprite")
    element.ref.init(this.images.get(image))
    element.initX = initX
    element.initY = initY
    if (withScale) {
      element.initScaleX = width * this.mapScale / element.ref.width
      element.initScaleY = height * this.mapScale / element.ref.height
    } else {
      element.initScaleX = width / element.ref.width
      element.initScaleY = height / element.ref.height
    }
    element.ref.scaleX = element.initScaleX
    element.ref.scaleY = element.initScaleY
    element.ref.x = element.initX
    element.ref.y = element.initY
    this.attachProperties(element)
    return this.cartoonElementsBuffer[id]
  }

  // 创建图片（回调方式）
  createCartoonElementImageFunc(id: string, image: string, callbackScale: Function, callbackPosition: Function) {
    let element = this.createCartoonElement(id, "MySprite")
    element.ref.init(this.images.get(image))
    element.rePosition = () => {
      let scale = callbackScale(element.ref.width, element.ref.height)
      let position = callbackPosition(element.ref.width, element.ref.height)
      // save initScale
      element.ref.initScaleX = scale.sx
      element.ref.initScaleY = scale.sy
      element.initScaleX = scale.sx
      element.initScaleY = scale.sy

      // save initX_Y
      element.ref.initX = position.x
      element.ref.initY = position.y
      element.initX = position.x
      element.initY = position.y

      // save initRotation
      element.ref.initRotation = element.rotation ? element.rotation : 0
      element.initRotation = element.rotation ? element.rotation : 0

      element.ref.scaleX = scale.sx
      element.ref.scaleY = scale.sy
      element.ref.x = element.initX
      element.ref.y = element.initY
    }
    element.rePosition()
    this.attachProperties(element)
    return this.cartoonElementsBuffer[id]
  }

  createCartoonElementLabel(id: string, text: string, fontName?: string, fontColor?: string, fontSize?: number, initX?: number, initY?: number) {
    if (!fontName) {
      fontName = "BRLNSDB"
    }
    if (!fontColor) {
      fontColor = "#000000"
    }
    if (!fontSize) {
      fontSize = 20
    }
    if (!initX) {
      initX = 0
    }
    if (!initY) {
      initY = 0
    }

    let element = this.createCartoonElement(id, "Label")
    element.ref.text = text
    element.ref.fontName = fontName
    element.ref.fontColor = fontColor
    element.ref.fontSize = fontSize
    element.ref.x = initX
    element.ref.y = initY
    element.ref.textAlign = "center"
    element.ref.refreshSize();

    this.attachProperties(element)
    return element
  }

  createCartoonElementLabelFunc(id: string, text: string, fontName?: string, fontColor?: string, fontSize?: number, callback?) {
    let element = this.createCartoonElement(id, "Label")
    element.ref.text = text
    element.ref.fontName = fontName
    element.ref.fontColor = fontColor
    element.ref.fontSize = fontSize
    element.ref.textAlign = "center"
    element.ref.refreshSize();

    if (callback) {
      let size = callback(element.ref.width, element.ref.height)
      element.ref.x = size.x
      element.ref.y = size.y
    }

    return element
  }

  createLabel = (text, fontName, fontColor, fontSize, initX?, initY?) => {
    let element = new Label()
    element.text = text;
    if (fontName) {
      element.fontName = fontName;
    }
    if (fontColor) {
      element.fontColor = fontColor;
    }
    if (fontSize) {
      element.fontSize = fontSize;
    }
    element.textAlign = "center";
    element.x = initX;
    element.y = initY;
    return element
  }

  createImage(image, callbackScale, callbackPosition) {
    let element = new MySprite()
    element.init(this.images.get(image))

    let scale = callbackScale(element.width, element.height)
    let position = callbackPosition(element.width, element.height)

    element.scaleX = scale.sx
    element.scaleY = scale.sy
    element.x = position.x
    element.y = position.y
    element.initScaleX = scale.sx
    element.initScaleY = scale.sy
    element.initX = position.x
    element.initY = position.y

    return element
  }

  createBorder(config) {
    let element = new ShapeRectNew()
    if(config.width) element.width = config.width
    if(config.height) element.height = config.height
    if(config.x) element.x = config.x
    if(config.y) element.y =config.y
    if(config.borderColor && config.lineWidth) element.setOutLine(config.borderColor, config.lineWidth)
    if(config.fill){
      element.fill = true
    }else{
      element.fill = false
    }
    if(config.radius) element.radius = config.radius
    return element
  }

  createRectangula(config) {
    let element = new ShapeRectNew()
    element.fill = true
    if(config.width) element.width = config.width
    if(config.height) element.height = config.height
    if(config.x) element.x = config.x
    if(config.y) element.y = config.y
    if(config.fillColor) element.fillColor = config.fillColor
    if(config.radius) element.radius = config.radius
    return element
  }

  createAnimation(imageKey, length, runTime, endCallback?, order?:boolean) {
    let element = new MyAnimation()
    element.id = "ANI-" + imageKey + "-" + Math.floor(Math.random()*10000)
    if(order){
      for (let index = length; index > 0; index--) {
        element.addFrameByImg(this.images.get(`${imageKey} (${index})`))
      }
    }else{
      for (let index = 0; index < length; index++) {
        element.addFrameByImg(this.images.get(`${imageKey} (${index + 1})`))
      }
    }

    element.delayPerUnit = (runTime / length) / 1000
    if (element.delayPerUnit > 1) {
      element.delayPerUnit = 1
    } else if (element.delayPerUnit < 0.01) {
      element.delayPerUnit = 0.01;
    }

    element.playEndFunc = () => {
      endCallback && endCallback()
    }

    return element
  }

  attachProperties(cartoonElement){
    // cartoonElement.updateBoundingBox = ()=>{
    //   cartoonElement.boundingBox = cartoonElement.ref.getBoundingBox()
    // }
    // cartoonElement.boundingBox = cartoonElement.ref.getBoundingBox()
  }

  getCartoonElementRef = (key) => {
    if (this.cartoonElementsBuffer[key]) {
      return this.cartoonElementsBuffer[key].ref;
    } else {
      return undefined
    }
  }
  getCartoonElement = (key) => {
    return this.cartoonElementsBuffer[key]
  }
  getCartoonElementsRef = (keys) => {
    let allElelemts = []
    keys.forEach(id => {
      if (this.cartoonElementsBuffer[id]) {
        allElelemts.push(this.cartoonElementsBuffer[id].ref)
      }
    });
    return allElelemts
  }
  getCartoonElements = (keys) => {
    let allElelemts = []
    keys.forEach(id => {
      if (this.cartoonElementsBuffer[id]) {
        allElelemts.push(this.cartoonElementsBuffer[id])
      }
    });
    return allElelemts
  }
  getAllCartoonElement = () => {
    return this.cartoonElementsBuffer
  }


  setCartoonElementPosition = (key, posi) => {
    this.cartoonElementsBuffer[key].ref.x = posi.x
    this.cartoonElementsBuffer[key].ref.y = posi.y
    this.cartoonElementsBuffer[key].x = posi.x
    this.cartoonElementsBuffer[key].y = posi.y
  }
  setCartoonElementPositionX = (key, x) => {
    this.cartoonElementsBuffer[key].ref.x = x
    this.cartoonElementsBuffer[key].x = x
  }
  setCartoonElementRelativePositionX = (key, x) => {
    this.cartoonElementsBuffer[key].relativeX = x
  }
  setCartoonElementPositionY = (key, y) => {
    this.cartoonElementsBuffer[key].ref.y = y
    this.cartoonElementsBuffer[key].y = y
  }
  setCartoonElementRelativePositionY = (key, y) => {
    this.cartoonElementsBuffer[key].relativeY = y
  }

  getCartoonElementPosition = (key) => {
    return {
      x: this.cartoonElementsBuffer[key].x,
      y: this.cartoonElementsBuffer[key].y
    }
  }
  getCartoonElementRelativePosition = (key) => {
    return {
      x: this.cartoonElementsBuffer[key].relativeX,
      y: this.cartoonElementsBuffer[key].relativeY
    }
  }

  saveSize(key){
    this.cartoonElementsBuffer[key].initX = this.cartoonElementsBuffer[key].ref.x
    this.cartoonElementsBuffer[key].initY = this.cartoonElementsBuffer[key].ref.y
    this.cartoonElementsBuffer[key].initScaleX = this.cartoonElementsBuffer[key].ref.scaleX
    this.cartoonElementsBuffer[key].initScaleY = this.cartoonElementsBuffer[key].ref.scaleX
    this.cartoonElementsBuffer[key].ref.initX = this.cartoonElementsBuffer[key].ref.x
    this.cartoonElementsBuffer[key].ref.initY = this.cartoonElementsBuffer[key].ref.y
    this.cartoonElementsBuffer[key].ref.initScaleX = this.cartoonElementsBuffer[key].ref.scaleX
    this.cartoonElementsBuffer[key].ref.initScaleY = this.cartoonElementsBuffer[key].ref.scaleX
  }
  
  resetAll = () => {
    this.cartoonElementsBuffer = {}
  }

}