import TWEEN from "@tweenjs/tween.js";
import simplexNoise from "../../assets/libs/simplex-noise/simplex-noise.min.js"
import { del } from "selenium-webdriver/http";
import construct = Reflect.construct;

class Sprite {
  x = 0;
  y = 0;
  color = "";
  radius = 0;
  alive = false;
  margin = 0;
  angle = 0;
  ctx;
  id;
  constructor(ctx = null) {
    if (!ctx) {
      this.ctx = window["curCtx"];
    } else {
      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;
  skewX = 0;
  skewY = 0;

  children = [this];

  childDepandVisible = true;
  childDepandAlpha = false;

  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.childDepandVisible) {
      return;
    }
    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;
    this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);
  }

  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) {
        if (this.visible) {
          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;
    });

    if (this.childDepandAlpha) {
      child.alpha = this.alpha;
    }
  }

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

  removeChildren() {
    for (let i = 0; i < this.children.length; i++) {
      if (this.children[i]) {
        if (this.children[i] != this) {
          this.children.splice(i, 1);
          i--;
        }
      }
    }
  }

  _changeChildAlpha(alpha) {
    for (let i = 0; i < this.children.length; i++) {
      if (this.children[i] != this) {
        this.children[i].alpha = alpha;
      }
    }
  }

  refreshAnchorOff() {
    this._offX = -this._width * this.anchorX;
    this._offY = -this._height * this.anchorY;
  }

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

  getBoundingBox() {
    const getParentData = item => {
      let px = item.x;
      let py = item.y;

      let sx = item.scaleX;
      let sy = item.scaleY;
      const parent = item.parent;
      if (parent) {
        const obj = getParentData(parent);
        const _x = obj.px;
        const _y = obj.py;
        const _sx = obj.sx;
        const _sy = obj.sy;
        px = _x + item.x * _sx;
        py = _y + item.y * _sy;
        sx *= _sx;
        sy *= _sy;
      }
      return { px, py, sx, sy };
    };

    const data = getParentData(this);
    const x = data.px + this._offX * Math.abs(data.sx);
    const y = data.py + this._offY * Math.abs(data.sy);
    const width = this.width * Math.abs(data.sx);
    const height = this.height * Math.abs(data.sy);

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

  set alpha(v) {
    this._alpha = v;
    if (this.childDepandAlpha) {
      this._changeChildAlpha(v);
    }
  }
  get alpha() {
    return this._alpha;
  }
  set width(v) {
    this._width = v;
    this.refreshAnchorOff();
  }
  get width() {
    return this._width;
  }
  set height(v) {
    this._height = v;
    this.refreshAnchorOff();
  }
  get height() {
    return this._height;
  }
  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;
  }

}

export class waterWave extends MySprite {
  bottole_r = 100;
  bottole_x0 = 100;
  bottole_y0 = 100;
  simplex = new simplexNoise()
  amp = 10; //波浪幅度 可以通过函数传递参数更改不同的幅度
  count = 80;
  speedY = 0;
  speedX = 0;
  height = this.bottole_r
  water_color = "red"
  per = 1.5
  _runTimeCtl = new Date().getTime()
  draw_self(color, comp, height) {
    this.ctx.beginPath();
    let r = this.bottole_r
    let a = this.bottole_x0 // this.bottole_r*2
    let b = this.bottole_y0 // this.bottole_r*2
    for (var i = 0; i <= this.count; i++) {
      this.speedX += 0.05;
      var x = a - r + i * (r * 2 / this.count);
      var y = b + r - height + this.simplex.noise2D(this.speedX, this.speedY) * this.amp;
      if (x > (a + r)) {
        x = a + r
      }
      if (x < (a - r)) {
        x = a - r
      }

      let c_y1 = Math.sqrt(r * r - (x - a) * (x - a)) + b
      let c_y2 = -Math.sqrt(r * r - (x - a) * (x - a)) + b
      if (y > c_y1) {
        y = c_y1
      }
      if (y < c_y2) {
        y = c_y2
      }
      this.ctx[i === 0 ? "moveTo" : "lineTo"](x, y);
    }
    this.ctx.arc(a, b, r, 0, Math.PI);
    this.ctx.closePath();
    this.ctx.fillStyle = color;
    this.ctx.fill();
  }

  drawSelf() {
    super.drawSelf();
    this.speedX = 0;
    if (new Date().getTime() - this._runTimeCtl > 10) {
      this.speedY += 0.02; //每次渲染需要更新波峰波谷值
    }
    this._runTimeCtl = new Date().getTime()
    this.draw_self(this.water_color, "screen", this.height);
  }
}

export class ColorSpr extends MySprite {
  r = 0;
  g = 0;
  b = 0;

  createGSCanvas() {
    if (!this.img) {
      return;
    }

    const rect = this.getBoundingBox();
    if (rect.width <= 1 || rect.height <= 1) {
      return;
    }

    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
    for (let i = 0; i < c.height; i++) {
      for (let j = 0; j < c.width; j++) {
        const x = i * 4 * c.width + j * 4;
        const r = c.data[x];
        const g = c.data[x + 1];
        const b = c.data[x + 2];

        c.data[x] = this.r;
        c.data[x + 1] = this.g;
        c.data[x + 2] = this.b;

      }
    }

    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
  }

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

export class GrayscaleSpr extends MySprite {
  grayScale = 120;

  createGSCanvas() {
    if (!this.img) {
      return;
    }

    const rect = this.getBoundingBox();
    const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
    for (let i = 0; i < c.height; i++) {
      for (let j = 0; j < c.width; j++) {
        const x = i * 4 * c.width + j * 4;
        const r = c.data[x];
        const g = c.data[x + 1];
        const b = c.data[x + 2];
        c.data[x] = c.data[x + 1] = c.data[x + 2] = this.grayScale; // (r + g + b) / 3;
      }
    }

    this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
  }

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

export class BitMapLabel extends MySprite {
  labelArr;
  baseUrl;

  setText(data, text) {
    this.labelArr = [];

    const labelArr = [];
    const tmpArr = text.split("");
    let totalW = 0;
    let h = 0;
    for (let i = 0; i < tmpArr.length; i++) {
      const label = new MySprite(this.ctx);
      label.init(data[tmpArr[i]], 0);
      this.addChild(label);
      labelArr.push(label);

      totalW += label.width;
      h = label.height;
    }

    this.width = totalW;
    this.height = h;

    let offX = -totalW / 2;
    for (let i = 0; i < labelArr.length; i++) {
      labelArr[i].x = offX;
      offX += labelArr[i].width;
    }

    this.labelArr = labelArr;
  }
}

export class Label extends MySprite {
  text: String;
  // fontSize:String = '40px';
  fontName: String = "Verdana";
  textAlign: String = "left";
  fontSize = 40;
  fontColor = "#000000";
  fontWeight = 900;
  maxWidth;
  outline = 0;
  outlineColor = "#ffffff";
  maxSingalLineWidth = 0;
  _shadowFlag = false;
  _shadowColor;
  _shadowOffsetX;
  _shadowOffsetY;
  _shadowBlur;

  _outlineFlag = false;
  _outLineWidth;
  _outLineColor;
  _warpLineY = 0;
  constructor(ctx = null) {
    super(ctx);
    this.init();
  }

  refreshSize() {
    this.ctx.save();

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

    this._width = this.ctx.measureText(this.text).width;
    this._height = this.fontSize;
    this.refreshAnchorOff();

    this.ctx.restore();
  }

  setMaxSize(w) {
    this.maxWidth = w;
    this.refreshSize();
    if (this.width >= w) {
      this.scaleX *= w / this.width;
      this.scaleY *= w / this.width;
    }
  }

  show(callBack = null) {
    this.visible = true;

    if (this.alpha >= 1) {
      return;
    }

    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.
  }

  setShadow(offX = 2, offY = 2, blur = 2, color = "rgba(0, 0, 0, 0.2)") {
    this._shadowFlag = true;
    this._shadowColor = color;
    // 将阴影向右移动15px，向上移动10px
    this._shadowOffsetX = offX;
    this._shadowOffsetY = offY;
    // 轻微模糊阴影
    this._shadowBlur = blur;
  }

  setOutline(width = 5, color = "#ffffff") {
    this._outlineFlag = true;
    this._outLineWidth = width;
    this._outLineColor = color;
  }

  drawText() {
    // console.log('in drawText', this.text);

    if (!this.text) {
      return;
    }

    if (this._shadowFlag) {
      this.ctx.shadowColor = this._shadowColor;
      // 将阴影向右移动15px，向上移动10px
      this.ctx.shadowOffsetX = this._shadowOffsetX;
      this.ctx.shadowOffsetY = this._shadowOffsetY;
      // 轻微模糊阴影
      this.ctx.shadowBlur = this._shadowBlur;
    }

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

    if (this._outlineFlag) {
      this.ctx.lineWidth = this._outLineWidth;
      this.ctx.strokeStyle = this._outLineColor;
      this.ctx.strokeText(this.text, 0, 0);
    }

    this.ctx.fillStyle = this.fontColor;

    if (this.outline > 0) {
      this.ctx.lineWidth = this.outline;
      this.ctx.strokeStyle = this.outlineColor;
      this.ctx.strokeText(this.text, 0, 0);
    }


    // 当maxSingalLineWidth不为0时，对数据进行换行处理
    if (this.maxSingalLineWidth !== 0) {
      var words = this.text.split(' ');
      var line = '';
      this._warpLineY = 0;
      for (var n = 0; n < words.length; n++) {
        var testLine = line + words[n] + ' ';
        var metrics = this.ctx.measureText(testLine);
        var testWidth = metrics.width;
        if (testWidth > this.maxSingalLineWidth && n > 0) {
          this.ctx.fillText(line, 0, this._warpLineY);
          line = words[n] + ' ';
          this._warpLineY += this.fontSize;
        }
        else {
          line = testLine;
        }
      }
      this.y = -this._warpLineY / 2
      this.ctx.fillText(line, 0, this._warpLineY);
    } else {
      this.ctx.fillText(this.text, 0, 0);
    }
  }

  drawSelf() {
    super.drawSelf();
    this.drawText();
  }
}
export class ShapeRectNew extends MySprite {


  radius = 0;
  fillColor = '#ffffff';
  strokeColor = '#000000';
  fill = true;
  stroke = false;
  lineWidth = 1;

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

  setOutLine(color, lineWidth) {
    this.stroke = true;
    this.strokeColor = color;
    this.lineWidth = lineWidth;
  }


  drawShape() {

    const ctx = this.ctx;
    const width = this.width;
    const height = this.height;
    const radius = this.radius;

    ctx.save();
    ctx.beginPath(0);
    // 从右下角顺时针绘制，弧度从0到1/2PI
    ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);

    // 矩形下边线
    ctx.lineTo(radius, height);

    // 左下角圆弧，弧度从1/2PI到PI
    ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);

    // 矩形左边线
    ctx.lineTo(0, radius);

    // 左上角圆弧，弧度从PI到3/2PI
    ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);

    // 上边线
    ctx.lineTo(width - radius, 0);

    // 右上角圆弧
    ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);

    // 右边线
    ctx.lineTo(width, height - radius);
    ctx.closePath();

    if (this.fill) {
      ctx.fillStyle = this.fillColor;
      ctx.fill();
    }
    if (this.stroke) {
      ctx.lineWidth = this.lineWidth;
      ctx.strokeStyle = this.strokeColor;
      ctx.stroke();
    }
    ctx.restore();

  }


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

}

export class RichTextOld extends Label {
  textArr = [];
  fontSize = 40;

  setText(text: string, words) {
    let newText = text;
    for (let i = 0; i < words.length; i++) {
      const word = words[i];

      const re = new RegExp(word, "g");
      newText = newText.replace(re, `#${word}#`);
      // newText = newText.replace(word, `#${word}#`);
    }

    this.textArr = newText.split("#");
    this.text = newText;

    // this.setSize();
  }

  refreshSize() {
    this.ctx.save();

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

    let curX = 0;
    for (let i = 0; i < this.textArr.length; i++) {
      const w = this.ctx.measureText(this.textArr[i]).width;
      curX += w;
    }

    this.width = curX;
    this.height = this.fontSize;
    this.refreshAnchorOff();

    this.ctx.restore();
  }

  show(callBack = null) {
    // console.log(' in show ');
    this.visible = true;
    // this.alpha = 0;

    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.
  }

  drawText() {
    // console.log('in drawText', this.text);

    if (!this.text) {
      return;
    }

    this.ctx.font = `${this.fontSize}px ${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);

    let curX = 0;
    for (let i = 0; i < this.textArr.length; i++) {
      const w = this.ctx.measureText(this.textArr[i]).width;

      if ((i + 1) % 2 == 0) {
        this.ctx.fillStyle = "#c8171e";
      } else {
        this.ctx.fillStyle = "#000000";
      }

      this.ctx.fillText(this.textArr[i], curX, 0);
      curX += w;
    }
  }
}

export class RichText extends Label {
  disH = 30;

  constructor(ctx) {
    super(ctx);

    // this.dataArr = dataArr;
  }

  drawText() {
    if (!this.text) {
      return;
    }

    this.ctx.font = `${this.fontSize}px ${this.fontName}`;
    this.ctx.textAlign = this.textAlign;
    this.ctx.textBaseline = "middle";
    this.ctx.fontWeight = this.fontWeight;
    this.ctx.fillStyle = this.fontColor;

    const selfW = this.width * this.scaleX;

    const chr = this.text.split(" ");
    let temp = "";
    const row = [];
    const w = selfW - 80;
    const disH = (this.fontSize + this.disH) * this.scaleY;

    for (let a = 0; a < chr.length; a++) {
      if (
        this.ctx.measureText(temp).width < w &&
        this.ctx.measureText(temp + chr[a]).width <= w
      ) {
        temp += " " + chr[a];
      } else {
        row.push(temp);
        temp = " " + chr[a];
      }
    }
    row.push(temp);

    const x = 0;
    const y = (-row.length * disH) / 2;
    // for (let b = 0 ; b < row.length; b++) {
    //   this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
    // }

    if (this._outlineFlag) {
      this.ctx.lineWidth = this._outLineWidth;
      this.ctx.strokeStyle = this._outLineColor;
      for (let b = 0; b < row.length; b++) {
        this.ctx.strokeText(row[b], x, y + (b + 1) * disH); // 每行字体y坐标间隔20
      }
      // this.ctx.strokeText(this.text, 0, 0);
    }

    // this.ctx.fillStyle = '#ff7600';
    for (let b = 0; b < row.length; b++) {
      this.ctx.fillText(row[b], x, y + (b + 1) * disH); // 每行字体y坐标间隔20
    }
  }

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

export class ShapeRect extends MySprite {
  fillColor = "#FF0000";

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

    // console.log('w:', w);
    // console.log('h:', 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 ShapeCircle extends MySprite {
  fillColor = "#FFFF00";
  radius = 0;
  startRadian = 0;
  endRadian = 180;
  strokeLineWidth = 5;
  strokeColor = "#702dee";
  drawType = "fill"
  counterclockwise = false; // false 逆时针 true 顺时针
  shadowColor = "rgba(0,0,0,0)"
  shadowOffsetX = 0;
  shadowOffsetY = 0;

  setRadius(r) {
    this.anchorX = this.anchorY = 0.5;
    this.radius = r;
    this.width = r * 2;
    this.height = r * 2;
  }

  drawShape() {
    switch (this.drawType) {
      case "stroke":
        this.ctx.beginPath();
        this.ctx.strokeStyle = this.strokeColor;
        this.ctx.lineWidth = this.strokeLineWidth
        this.ctx.arc(0, 0, this.radius, this.startRadian, this.endRadian, this.counterclockwise);
        this.ctx.stroke()
        break;
      default:
        this.ctx.beginPath();
        this.ctx.fillStyle = this.fillColor;
        this.ctx.arc(0, 0, this.radius, this.startRadian, this.endRadian);
        this.ctx.shadowColor = this.shadowColor
        this.ctx.shadowOffsetX = this.shadowOffsetX
        this.ctx.shadowOffsetY = this.shadowOffsetY
        this.ctx.fill();
        break;
    }

  }

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

export class MyAnimation extends MySprite {
  frameArr = [];
  frameIndex = 0;
  playFlag = false;
  lastDateTime;
  curDelay = 0;

  loop = false;
  playEndFunc;
  delayPerUnit = 1;

  restartFlag = false;
  reverseFlag = false;

  addFrameByImg(img) {
    const spr = new MySprite(this.ctx);
    spr.init(img);
    this._refreshSize(img);

    spr.visible = false;

    this.addChild(spr);
    this.frameArr.push(spr);

    this.frameArr[this.frameIndex].visible = true;
  }

  addFrameByUrl(url) {
    const spr = new MySprite(this.ctx);
    spr.load(url).then(img => {
      this._refreshSize(img);
    });
    spr.visible = false;

    this.addChild(spr);
    this.frameArr.push(spr);

    this.frameArr[this.frameIndex].visible = true;
  }

  _refreshSize(img) {
    if (this.width < img["width"]) {
      this.width = img["width"];
    }
    if (this.height < img["height"]) {
      this.height = img["height"];
    }
  }

  play() {
    this.playFlag = true;
    this.lastDateTime = new Date().getTime();
  }

  stop() {
    this.playFlag = false;
  }

  replay() {
    this.restartFlag = true;
    this.play();
  }

  reverse() {
    this.reverseFlag = !this.reverseFlag;
    this.frameArr.reverse();
    this.frameIndex = 0;
  }

  showAllFrame() {
    for (let i = 0; i < this.frameArr.length; i++) {
      this.frameArr[i].alpha = 1;
    }
  }

  hideAllFrame() {
    for (let i = 0; i < this.frameArr.length; i++) {
      this.frameArr[i].alpha = 0;
    }
  }

  playEnd() {
    this.playFlag = false;
    this.curDelay = 0;

    this.frameArr[this.frameIndex].visible = true;

    if (this.playEndFunc) {
      this.playEndFunc();
      this.playEndFunc = null;
    }
  }

  updateFrame() {
    if (this.frameArr[this.frameIndex]) {
      this.frameArr[this.frameIndex].visible = false;
    }

    this.frameIndex++;
    if (this.frameIndex >= this.frameArr.length) {
      if (this.loop) {
        this.frameIndex = 0;
      } else if (this.restartFlag) {
        this.restartFlag = false;
        this.frameIndex = 0;
      } else {
        this.frameIndex--;
        this.playEnd();
        return;
      }
    }

    this.frameArr[this.frameIndex].visible = true;
  }

  _updateDelay(delay) {
    this.curDelay += delay;
    if (this.curDelay < this.delayPerUnit) {
      return;
    }
    this.curDelay -= this.delayPerUnit;
    this.updateFrame();
  }

  _updateLastDate() {
    if (!this.playFlag) {
      return;
    }

    let delay = 0;
    if (this.lastDateTime) {
      delay = (new Date().getTime() - this.lastDateTime) / 1000;
    }
    this.lastDateTime = new Date().getTime();
    this._updateDelay(delay);
  }

  update($event: any = null) {
    super.update($event);
    this._updateLastDate();
  }
}

// --------===========  util func  =============-------------

export function tweenChange(
  item,
  obj,
  time = 0.8,
  callBack = null,
  easing = null,
  update = null
) {
  const tween = new TWEEN.Tween(item).to(obj, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }
  if (update) {
    tween.onUpdate((a, b) => {
      update(a, b);
    });
  }

  tween.start();
  return tween;
}

export function rotateItem(
  item,
  rotation,
  time = 0.8,
  callBack = null,
  easing = null
) {
  const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}

export function scaleItem(
  item,
  scale,
  time = 0.8,
  callBack = null,
  easing = null
) {
  const tween = new TWEEN.Tween(item).to(
    { scaleX: scale, scaleY: scale },
    time * 1000
  );

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }

  tween.start();
  return tween;
}

export function moveItem(
  item,
  x,
  y,
  time = 0.8,
  callBack = null,
  easing = null
) {
  const tween = new TWEEN.Tween(item).to({ x, y }, time * 1000);

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }

  tween.start();

  return tween;
}

export function endShow(item, s = 1) {
  item.scaleX = item.scaleY = 0;
  item.alpha = 0;

  const tween = new TWEEN.Tween(item)
    .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();
}

export function hideItem(item, time = 0.8, callBack = null, easing = null) {
  if (item.alpha == 0) {
    return;
  }

  const tween = new TWEEN.Tween(item)
    .to({ alpha: 0 }, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(function () {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}

export function showItem(item, time = 0.8, callBack = null, easing = null) {
  if (item.alpha == 1) {
    if (callBack) {
      callBack();
    }
    return;
  }

  item.visible = true;
  const tween = new TWEEN.Tween(item)
    .to({ alpha: 1 }, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(function () {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}

export function alphaItem(
  item,
  alpha,
  time = 0.8,
  callBack = null,
  easing = null
) {
  const tween = new TWEEN.Tween(item)
    .to({ alpha: alpha }, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(function () {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}

export function showStar(item, time = 0.8, callBack = null, easing = null) {
  const tween = new TWEEN.Tween(item)
    .to({ alpha: 1, scale: 1 }, time * 1000)
    // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
    .onComplete(function () {
      if (callBack) {
        callBack();
      }
    });

  if (easing) {
    tween.easing(easing);
  }

  tween.start();
}

export function randomSortByArr(arr) {
  const newArr = [];
  const tmpArr = arr.concat();
  while (tmpArr.length > 0) {
    const randomIndex = Math.floor(tmpArr.length * Math.random());
    newArr.push(tmpArr[randomIndex]);
    tmpArr.splice(randomIndex, 1);
  }
  return newArr;
}

export function radianToAngle(radian) {
  return (radian * 180) / Math.PI;
  // 角度 = 弧度 * 180 / Math.PI;
}

export function angleToRadian(angle) {
  return (angle * Math.PI) / 180;
  // 弧度= 角度 * Math.PI / 180;
}

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;
  }

  // console.log('angle: ', angle);
  return angle;
}

export function removeItemFromArr(arr, item) {
  const index = arr.indexOf(item);
  if (index != -1) {
    arr.splice(index, 1);
  }
}

export function circleMove(
  item,
  x0,
  y0,
  time = 2,
  addR = 360,
  xPer = 1,
  yPer = 1,
  callBack = null,
  easing = null
) {
  const r = getPosDistance(item.x, item.y, x0, y0);
  let a = getAngleByPos(item.x, item.y, x0, y0);
  a += 90;
  const obj = { r, a };

  item._circleAngle = a;
  const targetA = a + addR;

  const tween = new TWEEN.Tween(item).to(
    { _circleAngle: targetA },
    time * 1000
  );

  if (callBack) {
    tween.onComplete(() => {
      callBack();
    });
  }
  if (easing) {
    tween.easing(easing);
  }

  tween.onUpdate((item, progress) => {
    // console.log(item._circleAngle);
    const r = obj.r;
    const a = item._circleAngle;

    const x = x0 + r * xPer * Math.cos((a * Math.PI) / 180);
    const y = y0 + r * yPer * Math.sin((a * Math.PI) / 180);

    item.x = x;
    item.y = y;

    // obj.a ++;
  });

  tween.start();
}

export function getPosDistance(sx, sy, ex, ey) {
  const _x = ex - sx;
  const _y = ey - sy;
  const len = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2));
  return len;
}

export function delayCall(callback, second) {
  const tween = new TWEEN.Tween(this)
    .delay(second * 1000)
    .onComplete(() => {
      if (callback) {
        callback();
      }
    })
    .start();
}

export function getMinScale(item, maxLen) {
  const sx = maxLen / item.width;
  const sy = maxLen / item.height;
  const minS = Math.min(sx, sy);
  return minS;
}

export function jelly(item, time = 0.7) {
  if (item.jellyTween) {
    TWEEN.remove(item.jellyTween);
  }

  const t = time / 9;
  const baseSX = item.scaleX;
  const baseSY = item.scaleY;
  let index = 0;

  const run = () => {
    if (index >= arr.length) {
      item.jellyTween = null;
      return;
    }
    const data = arr[index];
    const t = tweenChange(
      item,
      { scaleX: data[0], scaleY: data[1] },
      data[2],
      () => {
        index++;
        run();
      },
      TWEEN.Easing.Sinusoidal.InOut
    );
    item.jellyTween = t;
  };

  const arr = [
    [baseSX * 1.1, baseSY * 0.9, t],
    [baseSX * 0.98, baseSY * 1.02, t * 2],
    [baseSX * 1.02, baseSY * 0.98, t * 2],
    [baseSX * 0.99, baseSY * 1.01, t * 2],
    [baseSX * 1.0, baseSY * 1.0, t * 2]
  ];

  run();
}

/**
 * 烟花爆炸效果动画
 * @param img 颗粒的图片
 * @param pos 爆点的坐标
 * @param parent 必须传一个父类
 */
export function showPopParticle(img, pos, parent) {
  const num = 20;
  const maxLen = 100;
  const minLen = 40;

  for (let i = 0; i < num; i++) {
    const particle = new MySprite();
    particle.init(img);
    particle.x = pos.x;
    particle.y = pos.y;
    parent.addChild(particle);

    const randomR = 360 * Math.random();
    particle.rotation = randomR;

    const randomS = 0.5 + Math.random() * 0.5;
    particle.setScaleXY(randomS);

    const randomX = Math.random() * 20 - 10;
    particle.x += randomX;

    const randomY = Math.random() * 20 - 10;
    particle.y += randomY;

    const randomL = minLen + Math.random() * maxLen;
    const randomA = 360 * Math.random();
    const randomT = getPosByAngle(randomA, randomL);
    moveItem(
      particle,
      particle.x + randomT.x,
      particle.y + randomT.y,
      0.4,
      () => { },
      TWEEN.Easing.Exponential.Out
    );

    scaleItem(particle, 0, 0.6, () => { });
  }
}

export function shake(item, time = 0.5, callback = null, rate = 1) {
  if (item.shakeTween) {
    return;
  }

  item.shakeTween = true;
  const offX = 15 * item.scaleX * rate;
  const offY = 15 * item.scaleX * rate;
  const baseX = item.x;
  const baseY = item.y;
  const easing = TWEEN.Easing.Sinusoidal.InOut;

  const move4 = () => {
    moveItem(
      item,
      baseX,
      baseY,
      time / 4,
      () => {
        item.shakeTween = false;
        if (callback) {
          callback();
        }
      },
      easing
    );
  };

  const move3 = () => {
    moveItem(
      item,
      baseX + offX / 4,
      baseY + offY / 4,
      time / 4,
      () => {
        move4();
      },
      easing
    );
  };

  const move2 = () => {
    moveItem(
      item,
      baseX - (offX / 4) * 3,
      baseY - (offY / 4) * 3,
      time / 4,
      () => {
        move3();
      },
      easing
    );
  };

  const move1 = () => {
    moveItem(
      item,
      baseX + offX,
      baseY + offY,
      time / 8,
      () => {
        move2();
      },
      easing
    );
  };

  move1();
}

// --------------- custom class --------------------
export function showBlingBling(starImg, rectArea, parent, mapScale = 1, num = 30, disTime = 0.1, showTime = 3) {
  const timeId = setInterval(() => {
    showBlingStar(starImg, num, rectArea, parent, mapScale);
  }, disTime * 1000);
  setTimeout(() => {
    clearInterval(timeId);
  }, showTime * 1000);

}

function showBlingStar(starImg, num, rectArea, parent, mapScale) {

  for (let i = 0; i < num; i++) {
    const star = new MySprite();
    star.init(starImg);

    const px = -parent.width / 2 + Math.random() * parent.width;
    const py = -parent.height / 2 + Math.random() * parent.height;
    star.x = px;
    star.y = py;

    const randomS = (0.1 + Math.random() * 0.8) * mapScale;
    star.setScaleXY(1);

    parent.addChild(star);


    scaleItem(star, randomS, 0.3, () => {

      setTimeout(() => {

        scaleItem(star, 0, 0.3, () => {
          parent.removeChild(star);
        });

      }, 100 + Math.random() * 200);
    });

  }


}