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


class Sprite {
  x = 0;
  y = 0;
  color = '';
  radius = 0;
  alive = false;
  margin = 0;
  angle = 0;
  ctx;

  constructor(ctx) {
    this.ctx = ctx;
  }
  update($event) {
    this.draw();
  }
  draw() {

  }

}





export class MySprite extends Sprite {

  width = 0;
  height = 0;
  _anchorX = 0;
  _anchorY = 0;
  _offX = 0;
  _offY = 0;
  scaleX = 1;
  scaleY = 1;
  alpha = 1;
  rotation = 0;
  visible = true;

  children = [this];

  img;
  _z = 0;


  init(imgObj = null, anchorX:number = 0.5, anchorY:number = 0.5) {

    if (imgObj) {

      this.img = imgObj;

      this.width = this.img.width;
      this.height = this.img.height;
    }

    this.anchorX = anchorX;
    this.anchorY = anchorY;
  }



  update($event = null) {
    if (this.visible) {
      this.draw();
    }
  }
  draw() {

    this.ctx.save();

    this.drawInit();

    this.updateChildren();

    this.ctx.restore();
  }

  drawInit() {

    this.ctx.translate(this.x, this.y);

    this.ctx.rotate(this.rotation * Math.PI / 180);

    this.ctx.scale(this.scaleX, this.scaleY);

    this.ctx.globalAlpha = this.alpha;

  }

  drawSelf() {
    if (this.img) {
      this.ctx.drawImage(this.img, this._offX, this._offY);
    }
  }

  updateChildren() {

    if (this.children.length <= 0) { return; }

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

      if (this.children[i] === this) {

        this.drawSelf();
      } else {

        this.children[i].update();
      }
    }
  }


  load(url, anchorX = 0.5, anchorY = 0.5) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.onerror = reject;
      img.src = url;
    }).then(img => {
      this.init(img, anchorX, anchorY);
      return img;
    });
  }

  addChild(child, z = 1) {
    if (this.children.indexOf(child) === -1) {
      this.children.push(child);
      child._z = z;
      child.parent = this;
    }

    this.children.sort((a, b) => {
      return a._z - b._z;
    });

  }
  removeChild(child) {
    const index = this.children.indexOf(child);
    if (index !== -1) {
      this.children.splice(index, 1);
    }
  }

  set anchorX(value) {
    this._anchorX = value;
    this.refreshAnchorOff();
  }
  get anchorX() {
    return this._anchorX;
  }
  set anchorY(value) {
    this._anchorY = value;
    this.refreshAnchorOff();
  }
  get anchorY() {
    return this._anchorY;
  }
  refreshAnchorOff() {
    this._offX = -this.width * this.anchorX;
    this._offY = -this.height * this.anchorY;
  }

  setScaleXY(value) {
    this.scaleX = this.scaleY = value;
  }

  getBoundingBox() {

    const x = this.x + this._offX * this.scaleX;
    const y = this.y + this._offY * this.scaleY;
    const width = this.width * this.scaleX;
    const height = this.height * this.scaleY;

    return {x, y, width, height};
  }

}





export class Item extends MySprite {

  baseX;

  move(targetY, callBack) {

    const self = this;

    const tween = new TWEEN.Tween(this)
      .to({ y: targetY }, 2500)
      .easing(TWEEN.Easing.Quintic.Out)
      .onComplete(function() {

        self.hide(callBack);
        // if (callBack) {
        //   callBack();
        // }
      })
      .start();

  }

  show(callBack = null) {

    const tween = new TWEEN.Tween(this)
      .to({ alpha: 1 }, 800)
      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
      .onComplete(function() {
        if (callBack) {
          callBack();
        }
      })
      .start(); // Start the tween immediately.

  }

  hide(callBack = null) {

    const tween = new TWEEN.Tween(this)
      .to({ alpha: 0 }, 800)
      // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
      .onComplete(function() {
        if (callBack) {
          callBack();
        }
      })
      .start(); // Start the tween immediately.
  }


  shake(id) {


    if (!this.baseX) {
      this.baseX = this.x;
    }

    const baseX = this.baseX;
    const baseTime = 50;
    const sequence = [
      {target: {x: baseX + 40 * id}, time: baseTime - 25},
      {target: {x: baseX - 20 * id}, time: baseTime},
      {target: {x: baseX + 10 * id}, time: baseTime},
      {target: {x: baseX - 5 * id}, time: baseTime},
      {target: {x: baseX + 2 * id}, time: baseTime},
      {target: {x: baseX - 1 * id}, time: baseTime},
      {target: {x: baseX}, time: baseTime},

    ];


    const self = this;

    function runSequence() {

      if (self['shakeTween']) {
        self['shakeTween'].stop();
      }

      const tween = new TWEEN.Tween(self);

      if (sequence.length > 0) {
        const action = sequence.shift();
        tween.to(action['target'], action['time']);
        tween.onComplete( () => {
          runSequence();
        });
        tween.start();
        self['shakeTween'] = tween;
      }
    }

    runSequence();

  }



  drop(targetY, callBack = null) {

    const self = this;

    const time = Math.abs(targetY - this.y) * 2.4;

    this.alpha = 1;

    const tween = new TWEEN.Tween(this)
      .to({ y: targetY }, time)
      .easing(TWEEN.Easing.Cubic.In)
      .onComplete(function() {

        // self.hideItem(callBack);
        if (callBack) {
          callBack();
        }
      })
      .start();


  }


}


export class EndSpr extends MySprite {

  show(s) {

    this.scaleX = this.scaleY = 0;
    this.alpha = 0;

    const tween = new TWEEN.Tween(this)
      .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
      .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
      .onComplete(function() {

      })
      .start(); // Start the tween immediately.

  }
}



export class ShapeRect extends MySprite {

  fillColor = '#FF0000';

  setSize(w, h) {
    this.width = w;
    this.height = h;
  }

  drawShape() {

    this.ctx.fillStyle = this.fillColor;
    this.ctx.fillRect(this._offX, this._offY, this.width, this.height);

  }


  drawSelf() {
    super.drawSelf();
    this.drawShape();
  }
}


export class HotZoneItem extends MySprite {

  lineDashFlag = false;
  arrow: MySprite;
  label: Label;
  text;

  arrowTop;
  arrowRight;

  setSize(w, h) {
    this.width = w;
    this.height = h;

    const rect = new ShapeRect(this.ctx);
    rect.x = -w / 2;
    rect.y = -h / 2;
    rect.setSize(w, h);
    rect.fillColor = '#FFFFFF';
    rect.alpha = 0.2;
    this.addChild(rect);
  }

  showLabel(text = null) {
    if (!this.label) {
      this.label = new Label(this.ctx);
      this.label.anchorY = 0;
      this.label.fontSize = '40px';
      this.label.textAlign = 'center';
      this.addChild(this.label);
      this.refreshLabelScale();
    }
    if (text) {
      this.label.text = text;
    } else if (this.text) {
      this.label.text = this.text;
    }
    this.label.visible = true;
  }

  hideLabel() {
    if (!this.label) { return; }
    this.label.visible = false;
  }

  refreshLabelScale() {
    if (this.scaleX == this.scaleY) {
      this.label.setScaleXY(1);
    }

    if (this.scaleX > this.scaleY) {
      this.label.scaleX = this.scaleY / this.scaleX;
    } else {
      this.label.scaleY = this.scaleX / this.scaleY;
    }
  }

  showLineDash() {
    this.lineDashFlag = true;

    if (this.arrow) {
      this.arrow.visible = true;
    } else {
      this.arrow = new MySprite(this.ctx);
      this.arrow.load('assets/common/arrow.png', 1, 0);
      this.arrow.setScaleXY(0.06);

      this.arrowTop = new MySprite(this.ctx);
      this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0);
      this.arrowTop.setScaleXY(0.06);

      this.arrowRight = new MySprite(this.ctx);
      this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5);
      this.arrowRight.setScaleXY(0.06);
    }
    this.showLabel();
  }

  hideLineDash() {
    this.lineDashFlag = false;
    if (this.arrow) {
      this.arrow.visible = false;
    }
    this.hideLabel();
  }



  drawArrow() {
    if (!this.arrow) { return; }
    const rect = this.getBoundingBox();
    this.arrow.x = rect.x + rect.width;
    this.arrow.y = rect.y;
    this.arrow.update();

    this.arrowTop.x = rect.x + rect.width / 2;
    this.arrowTop.y = rect.y;
    this.arrowTop.update();

    this.arrowRight.x = rect.x + rect.width;
    this.arrowRight.y = rect.y + rect.height / 2;
    this.arrowRight.update();
  }

  drawFrame() {
    this.ctx.save();
    const rect = this.getBoundingBox();
    const w = rect.width;
    const h = rect.height;
    const x = rect.x + w / 2;
    const y = rect.y + h / 2;
    this.ctx.setLineDash([5, 5]);
    this.ctx.lineWidth = 2;
    this.ctx.strokeStyle = '#1bfff7';
    this.ctx.beginPath();
    this.ctx.moveTo( x - w / 2, y - h / 2);
    this.ctx.lineTo(x + w / 2, y - h / 2);
    this.ctx.lineTo(x + w / 2, y + h / 2);
    this.ctx.lineTo(x - w / 2, y + h / 2);
    this.ctx.lineTo(x - w / 2, y - h / 2);
    this.ctx.stroke();
    this.ctx.restore();

  }

  draw() {
    super.draw();
    if (this.lineDashFlag) {
      this.drawFrame();
      this.drawArrow();
    }
  }
}



export class HotZoneImageItem extends MySprite {

  lineDashFlag = false;
  arrow: MySprite;
  label: Label;
  image: MySprite;
  image_url: String;
  labelBox: MySprite;
  audio_url: String;
  scale
  text;

  arrowTop;
  arrowRight;

  init(image_url = null, callback?){
    this.showImage(image_url, (img)=>{
      callback && callback(img.width, img.height)
      this.setSize(img.width*this.image.scaleX, img.height*this.image.scaleY)
      this.lineDashFlag = true;
      this.showLineDash()
      this.showLabel();
      this.drawArrow()
    })
  }
  
  setSize(w, h) {
    this.width = w;
    this.height = h;
  }

  showLabel(text = null) {
    if (!this.label) {
      this.labelBox = new MySprite(this.ctx);
      this.labelBox.load('assets/default/bg_50_50.png').then(()=>{
        this.labelBox.x = this.labelBox.width/2;
        this.labelBox.y = this.labelBox.height/2;
      });
      this.label = new Label(this.ctx);
      // this.label.anchorY = 0;
      this.label.fontSize = '30px';
      this.label.textAlign = 'center';
      this.label.color = "#FFFFFF"
      
      this.labelBox.addChild(this.label);
      this.addChild(this.labelBox);
      this.refreshLabelScale();
    }
    if (text) {
      this.label.text = text;
    } else if (this.text) {
      this.label.text = this.text;
    }
    this.label.visible = true;
  }

  hideLabel() {
    if (!this.label) { return; }
    this.label.visible = false;
  }

  showImage(image_url = null, callback) {
    this.loadImage(image_url).then((img)=>{
      if(this.image){
        this.removeChild(this.image)
      }
      this.image = new MySprite(this.ctx);
      this.image.init(img)
      this.image.x = this.image.width/2
      this.image.y = this.image.height/2
      this.image.alpha = 0.7;
      this.image_url = image_url;
      this.addChild(this.image,-1);
      callback && callback(this.image)
    })
  }

  loadImage(image_url = null){
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.onerror = reject;
      img.src = image_url;
    })
  }
  
  hideImage() {
    if (!this.image) { return; }
    this.image.visible = false;
  }

  refreshLabelScale() {
    this.labelBox.scaleX = 75 / (this.labelBox.width*this.image.scaleX)
  }

  showLineDash() {
    if (this.arrow) {
      this.arrow.visible = true;
    } else {
      this.arrow = new MySprite(this.ctx);
      this.arrow.load('assets/common/arrow.png', 1, 0);
      this.arrow.setScaleXY(0.06);

      this.arrowTop = new MySprite(this.ctx);
      this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0);
      this.arrowTop.setScaleXY(0.06);

      this.arrowRight = new MySprite(this.ctx);
      this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5);
      this.arrowRight.setScaleXY(0.06);
    }
  }

  hideLineDash() {
    this.lineDashFlag = false;
    if (this.arrow) {
      this.arrow.visible = false;
    }
    this.hideLabel();
  }



  drawArrow() {
    if (!this.arrow) { return; }
    const rect = this.getBoundingBox();
    this.arrow.x = rect.x + rect.width;
    this.arrow.y = rect.y;
    this.arrow.update();

    this.arrowTop.x = rect.x + rect.width / 2;
    this.arrowTop.y = rect.y;
    this.arrowTop.update();

    this.arrowRight.x = rect.x + rect.width;
    this.arrowRight.y = rect.y + rect.height / 2;
    this.arrowRight.update();
  }

  drawFrame() {
    this.ctx.save();
    const rect = this.getBoundingBox();
    const w = rect.width;
    const h = rect.height;
    const x = rect.x + w / 2;
    const y = rect.y + h / 2;
    this.ctx.setLineDash([5, 5]);
    this.ctx.lineWidth = 2;
    this.ctx.strokeStyle = '#1bfff7';
    this.ctx.beginPath();
    this.ctx.moveTo( x - w / 2, y - h / 2);
    this.ctx.lineTo(x + w / 2, y - h / 2);
    this.ctx.lineTo(x + w / 2, y + h / 2);
    this.ctx.lineTo(x - w / 2, y + h / 2);
    this.ctx.lineTo(x - w / 2, y - h / 2);
    this.ctx.stroke();
    this.ctx.restore();

  }

  draw() {
    super.draw();
    if (this.lineDashFlag) {
      this.drawFrame();
      this.drawArrow();
    }
  }
}


export class EditorItem extends MySprite {

  lineDashFlag = false;
  arrow: MySprite;
  label:Label;
  text;

  showLabel(text = null) {


    if (!this.label) {
      this.label = new Label(this.ctx);
      this.label.anchorY = 0;
      this.label.fontSize = '50px';
      this.label.textAlign = 'center';
      this.addChild(this.label);
      this.label.setScaleXY(1 / this.scaleX);
    }

    if (text) {
      this.label.text = text;
    } else if (this.text) {
      this.label.text = this.text;
    }
    this.label.visible = true;

  }

  hideLabel() {
    if (!this.label) { return; }

    this.label.visible = false;
  }

  showLineDash() {
    this.lineDashFlag = true;
    if (this.arrow) {
      this.arrow.visible = true;
    } else {
      this.arrow = new MySprite(this.ctx);
      this.arrow.load('assets/common/arrow.png', 1, 0);
      this.arrow.setScaleXY(0.06);
    }

    this.showLabel();
  }

  hideLineDash() {

    this.lineDashFlag = false;

    if (this.arrow) {
      this.arrow.visible = false;
    }

    this.hideLabel();
  }



  drawArrow() {
    if (!this.arrow) { return; }

    const rect = this.getBoundingBox();
    this.arrow.x = rect.x + rect.width;
    this.arrow.y = rect.y;

    this.arrow.update();
  }

  drawFrame() {


    this.ctx.save();


    const rect = this.getBoundingBox();

    const w = rect.width;
    const h = rect.height;
    const x = rect.x + w / 2;
    const y = rect.y + h / 2;

    this.ctx.setLineDash([5, 5]);
    this.ctx.lineWidth = 2;
    this.ctx.strokeStyle = '#1bfff7';
    // this.ctx.fillStyle = '#ffffff';

    this.ctx.beginPath();

    this.ctx.moveTo( x - w / 2, y - h / 2);

    this.ctx.lineTo(x + w / 2, y - h / 2);

    this.ctx.lineTo(x + w / 2, y + h / 2);

    this.ctx.lineTo(x - w / 2, y + h / 2);

    this.ctx.lineTo(x - w / 2, y - h / 2);

    // this.ctx.fill();
    this.ctx.stroke();




    this.ctx.restore();

  }

  draw() {
    super.draw();

    if (this.lineDashFlag) {
      this.drawFrame();
      this.drawArrow();
    }
  }
}



export class Label extends MySprite {

  text:String;
  fontSize:String = '40px';
  fontName:String = 'Verdana';
  textAlign:String = 'left';


  constructor(ctx) {
    super(ctx);
    this.init();
  }

  drawText() {

    if (!this.text) { return; }

    this.ctx.font = `${this.fontSize} ${this.fontName}`;
    this.ctx.textAlign = this.textAlign;
    this.ctx.textBaseline = 'middle';
    this.ctx.fontWeight = 900;

    this.ctx.lineWidth = 5;
    this.ctx.strokeStyle = '#ffffff';
    this.ctx.strokeText(this.text, 0, 0);

    this.ctx.fillStyle = '#000000';
    this.ctx.fillText(this.text, 0, 0);

  }


  drawSelf() {
    super.drawSelf();
    this.drawText();
  }

}



export function getPosByAngle(angle, len) {

  const radian = angle * Math.PI / 180;
  const x = Math.sin(radian) * len;
  const y = Math.cos(radian) * len;

  return {x, y};

}

export function getAngleByPos(px, py, mx, my) {

  const x = Math.abs(px - mx);
  const y = Math.abs(py - my);
  const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
  const cos = y / z;
  const radina = Math.acos(cos); // 用反三角函数求弧度
  let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度

  if(mx > px && my > py) {// 鼠标在第四象限
    angle = 180 - angle;
  }

  if(mx === px && my > py) {// 鼠标在y轴负方向上
    angle = 180;
  }

  if(mx > px && my === py) {// 鼠标在x轴正方向上
    angle = 90;
  }

  if(mx < px && my > py) {// 鼠标在第三象限
    angle = 180 + angle;
  }

  if(mx < px && my === py) {// 鼠标在x轴负方向
    angle = 270;
  }

  if(mx < px && my < py) {// 鼠标在第二象限
    angle = 360 - angle;
  }

  return angle;
}
