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 exchangeNodePos(baseNode, targetNode) {
  return baseNode.convertToNodeSpaceAR(targetNode._parent.convertToWorldSpaceAR(cc.v2(targetNode.x, targetNode.y)));
}

export function RandomInt(a, b = 0) {
  let max = Math.max(a, b);
  let min = Math.min(a, b);
  return Math.floor(Math.random() * (max - min) + min);
}

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 setSprNodeMaxLen(sprNode, maxW, maxH) {
  const sx = maxW / sprNode.width;
  const sy = maxH / sprNode.height;
  const s = Math.min(sx, sy);
  sprNode.scale = Math.round(s * 1000) / 1000;
}

export function localPosTolocalPos(baseNode, targetNode) {
  const worldPos = targetNode.parent.convertToWorldSpaceAR(cc.v2(targetNode.x, targetNode.y));
  const localPos = baseNode.parent.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y));
  return localPos;
}

export function worldPosToLocalPos(worldPos, baseNode) {
  const localPos = baseNode.parent.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y));
  return localPos;
}

export function getScaleRateBy2Node(baseNode, targetNode, maxFlag = true) {
  const worldRect1 = targetNode.getBoundingBoxToWorld();
  const worldRect2 = baseNode.getBoundingBoxToWorld();

  const sx = worldRect1.width / worldRect2.width;
  const sy = worldRect1.height / worldRect2.height;
  if (maxFlag) {
    return Math.max(sx, sy);
  } else {
    return Math.min(sx, sy);
  }
}

export function getDistance(start, end) {
  var pos = cc.v2(start.x - end.x, start.y - end.y);
  var dis = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
  return dis;
}

export function playAudioByUrl(audio_url, cb = null) {
  if (audio_url) {
    cc.assetManager.loadRemote(audio_url, (err, audioClip) => {
      const audioId = cc.audioEngine.play(audioClip, false, 0.8);
      if (cb) {
        cc.audioEngine.setFinishCallback(audioId, () => {
          cb();
        });
      }
    });
  }
}


export function btnClickAnima(btn, time = 0.15, rate = 1.05) {
  btn.tmpScale = btn.scale;
  btn.on(cc.Node.EventType.TOUCH_START, () => {
    cc.tween(btn)
      .to(time / 2, { scale: btn.scale * rate })
      .start()
  })
  btn.on(cc.Node.EventType.TOUCH_CANCEL, () => {
    cc.tween(btn)
      .to(time / 2, { scale: btn.tmpScale })
      .start()
  })
  btn.on(cc.Node.EventType.TOUCH_END, () => {
    cc.tween(btn)
      .to(time / 2, { scale: btn.tmpScale })
      .start()
  })
}


export function getSpriteFrimeByUrl(url, cb) {
  cc.loader.load({ url }, (err, img) => {
    const spriteFrame = new cc.SpriteFrame(img)
    if (cb) {
      cb(spriteFrame);
    }
  })
}

export function getSprNode(resName) {
  const sf = cc.find('Canvas/res/img/' + resName).getComponent(cc.Sprite).spriteFrame;
  const node = new cc.Node();
  node.addComponent(cc.Sprite).spriteFrame = sf;
  return node;
}

export function getSprNodeByUrl(url, cb) {
  const node = new cc.Node();
  const spr = node.addComponent(cc.Sprite);
  getSpriteFrimeByUrl(url, (sf) => {
    spr.spriteFrame = sf;
    if (cb) {
      cb(spr);
    }
  })
}



export function playAudio(audioClip, cb = null) {
  if (audioClip) {
    const audioId = cc.audioEngine.playEffect(audioClip, false);
    if (cb) {
      cc.audioEngine.setFinishCallback(audioId, () => {
        cb();
      });
    }
  }
}

export async function asyncDelay(time) {
  return new Promise((resolve, reject) => {
    try {
      cc.tween(cc.find('Canvas'))
        .delay(time)
        .call(() => {
          resolve(null);
        })
        .start();
    } catch (e) {
      reject(e);
    }
  })
}

export async function asyncLoadDragonBoneAnime(node, { skeJsonData: { url: skeJsonDataUrl }, texJsonData: { url: texJsonDataUrl }, texPngData: { url: texPngDataUrl } }) {

  if (!texPngDataUrl || !texJsonDataUrl || !texPngDataUrl
    || texPngDataUrl == '' || texJsonDataUrl == '' || texPngDataUrl == '') {
    return;
  }

  return new Promise((resolve, reject) => {
    if (node.animaNode) {
      node.animaNode.removeFromParent();
    }
    const animaNode = new cc.Node();
    animaNode.name = 'animaNode';
    animaNode.parent = node;
    animaNode.active = true;
    node.animaNode = animaNode;

    const dragonDisplay = animaNode.addComponent(dragonBones.ArmatureDisplay);

    const loadTexture = new Promise((resolve, reject) => {
      cc.assetManager.loadRemote(texPngDataUrl, (error, texture) => {
        if (error) {
          reject(error);
        }
        resolve(texture);
      });
    });

    const loadTexJsonData = new Promise((resolve, reject) => {
      cc.assetManager.loadAny({ url: texJsonDataUrl }, (error, atlasJson) => {
        if (error) {
          reject(error);
        }
        resolve(atlasJson);
      });
    });

    const loadSkeJsonData = new Promise((resolve, reject) => {
      cc.assetManager.loadAny({ url: skeJsonDataUrl }, (error, dragonBonesJson) => {
        if (error) {
          reject(error);
        }
        resolve(dragonBonesJson);
      });
    });

    Promise.all([loadTexture, loadTexJsonData, loadSkeJsonData]).then(([texture, atlasJson, dragonBonesJson]) => {
      const atlas = new dragonBones.DragonBonesAtlasAsset();
      atlas.atlasJson = JSON.stringify(atlasJson);
      atlas.texture = texture;

      const asset = new dragonBones.DragonBonesAsset();
      asset.dragonBonesJson = JSON.stringify(dragonBonesJson);

      dragonDisplay.dragonAtlasAsset = atlas;
      dragonDisplay.dragonAsset = asset;

      let armatureNames = dragonBonesJson.armature.map(data => data.name);

      if (armatureNames.length > 0) {
        dragonDisplay.armatureName = armatureNames[0];
      }
      resolve(animaNode);
    });
  });
}

export class FireworkSettings {
  baseNode; // 父节点
  nodeList; // 火花节点的array
  pos; // 发射点
  side; // 发射方向
  range; // 扩散范围
  number; // 发射数量
  scalseRange; // 缩放范围
  constructor(baseNode, nodeList,
    pos = cc.v2(0, 0),
    side = cc.v2(0, 100),
    range = 50,
    number = 100,
    scalseRange = 0
  ) {
    this.baseNode = baseNode;
    this.nodeList = nodeList;
    this.pos = pos;
    this.side = side;
    this.range = range;
    this.number = number;
    this.scalseRange = scalseRange;
  }

  static copy(firework) {
    return new FireworkSettings(
      firework.baseNode,
      firework.nodeList,
      firework.pos,
      firework.side,
      firework.range,
      firework.number,
    );
  }
}

export async function showFireworks(fireworkSettings) {
  const { baseNode, nodeList, pos, side, range, number, scalseRange } = fireworkSettings;
  new Array(number).fill(' ').forEach(async (_, i) => {

    let rabbonNode = new cc.Node();
    rabbonNode.parent = baseNode;
    rabbonNode.x = pos.x;
    rabbonNode.y = pos.y;
    rabbonNode.angle = 60 * Math.random() - 30;

    let node = cc.instantiate(nodeList[RandomInt(nodeList.length)]);
    node.parent = rabbonNode;
    node.active = true;
    node.x = 0;
    node.y = 0;
    node.angle = 0;
    node.scale = (Math.random() - 0.5) * scalseRange + 1;

    const rate = Math.random();
    const angle = Math.PI * (Math.random() * 2 - 1);

    await asyncTweenBy(rabbonNode, 0.3, {
      x: side.x * rate + Math.cos(angle) * range * rate,
      y: side.y * rate + Math.sin(angle) * range * rate
    }, {
      easing: 'quadIn'
    });

    cc.tween(rabbonNode)
      .by(8, { y: -2000 })
      .start();

    cc.tween(rabbonNode)
      .to(5, { scale: (Math.random() - 0.5) * scalseRange + 1 })
      .start();

    rabbonFall(rabbonNode);

    await asyncDelay(Math.random());
    cc.tween(node)
      .by(0.15, { x: -10, angle: -10 })
      .by(0.3, { x: 20, angle: 20 })
      .by(0.15, { x: -10, angle: -10 })
      .union()
      .repeatForever()
      .start();

    cc.tween(rabbonNode)
      .delay(5)
      .to(0.3, { opacity: 0 })
      .call(() => {
        node.stopAllActions();
        node.active = false;
        node.parent = null;
        node = null;
      })
      .start();
  });
}

async function rabbonFall(node) {
  const time = 1 + Math.random();
  const offsetX = RandomInt(-200, 200) * time;
  await asyncTweenBy(node, time, { x: offsetX, angle: offsetX * 60 / 200 });
  rabbonFall(node);
}


export async function asyncTweenTo(node, duration, obj, ease = undefined) {
  return new Promise((resolve, reject) => {
    try {
      cc.tween(node)
        .to(duration, obj, ease)
        .call(() => {
          resolve(null);
        })
        .start();
    } catch (e) {
      reject(e);
    }
  });
}

export async function asyncTweenBy(node, duration, obj, ease = undefined) {
  return new Promise((resolve, reject) => {
    try {
      cc.tween(node)
        .by(duration, obj, ease)
        .call(() => {
          resolve(null);
        })
        .start();
    } catch (e) {
      reject(e);
    }
  });
}

export function showTrebleFirework(baseNode, rabbonList) {
  const middle = new FireworkSettings(baseNode, rabbonList);
  middle.pos = cc.v2(0, -400);
  middle.side = cc.v2(0, 1000);
  middle.range = 200;
  middle.number = 100;
  middle.scalseRange = 0.4;

  const left = FireworkSettings.copy(middle);
  left.pos = cc.v2(-600, -400);
  left.side = cc.v2(200, 1000);

  const right = FireworkSettings.copy(middle);
  right.pos = cc.v2(600, -400);
  right.side = cc.v2(-200, 1000);

  showFireworks(middle);
  showFireworks(left);
  showFireworks(right);
}

export function onHomeworkFinish() {
  const middleLayer = cc.find('middleLayer');
  if (middleLayer) {
    const middleLayerComponent = middleLayer.getComponent('middleLayer');
    if (middleLayerComponent.role == 'student') {
      middleLayerComponent.onHomeworkFinish(() => { });
    }
  } else {
    console.log('onHomeworkFinish');
  }
}


export function asyncCallNetworkApiGet(apiName, data): Promise<any> {
  return new Promise((resolve, reject) => {
    callNetworkApiGet(apiName, data, (res => {
      resolve(res);
    }));
  });
}
export function asyncCallNetworkApiPost(uri, data): Promise<any> {
  return new Promise((resolve, reject) => {
    callNetworkApiPost(uri, data, (res) => {
      resolve(res);
    });
  });
}

export function callNetworkApiPost(uri, data, callBack) {
  const middleLayer = cc.find('middleLayer')?.getComponent('middleLayer');
  if (middleLayer) {
    middleLayer.callNetworkApiPost(uri, data, callBack);
    return;
  }
  const baseUrl = 'http://staging-openapi.iteachabc.com';
  const xhr = new XMLHttpRequest();
  const url = `${baseUrl}${uri}`;
  xhr.open("POST", url, true);
  xhr.setRequestHeader('content-type', 'application/json');
  xhr.onreadystatechange = () => {
    if (xhr.readyState == 4) {
      callBack(JSON.parse(xhr.responseText));
    }
  }
  xhr.send(JSON.stringify(data));
}

export function callNetworkApiGet(uri, data, callBack) {
  const middleLayer = cc.find('middleLayer')?.getComponent('middleLayer');
  if (middleLayer) {
    middleLayer.callNetworkApiGet(uri, data, callBack);
    return;
  }
  const baseUrl = 'http://staging-openapi.iteachabc.com';
  let queryStr = '?';
  const params = [];
  for (const key in data) {
    if (Object.hasOwnProperty.call(data, key)) {
      params.push(`${key}=${data[key]}`);
    }
  }
  queryStr += params.join("&");
  const xhr = new XMLHttpRequest();
  xhr.onreadystatechange = () => {
    if (xhr.readyState == 4 && (xhr.status >= 200 && xhr.status < 400)) {
      callBack(xhr.responseText);
    }
  };
  const url = `${baseUrl}${uri}${queryStr}`;
  console.log('url = ' + url);
  xhr.open('GET', url, true);
  xhr.send();
}

export function jumpToBundle(bundleName: string) {
  const middleLayer = cc.find('middleLayer');
  if (middleLayer) {
    const middleLayerComponent = middleLayer.getComponent('middleLayer');
    middleLayerComponent.loadOnlineBundle(bundleName);
  } else {
    console.log('jump to bundle: ' + bundleName);
  }
}

export function jumpToCourseWare(courseWareId: number) {
  const middleLayer = cc.find('middleLayer');
  if (middleLayer) {
    const middleLayerComponent = middleLayer.getComponent('middleLayer');
    middleLayerComponent.loadOnlineCourseWare(courseWareId);
  } else {
    console.log('jump to CourseWare: ' + courseWareId);
  }
}

export function buttonOnceClick(button: cc.Node, callback: Function, scale = 1.1) {
  button.once('click', () => {
    if (button['cantClick']) {
      return;
    }
    button['cantClick'] = true;
    cc.tween(button)
      .to(0.1, { scale: scale })
      .to(0.1, { scale: 1.0 })
      .call(() => {
        button['cantClick'] = false;
        callback && callback();
      })
      .start();
  });
}

export function buttonOnClick(button: cc.Node, callback: Function, scale = 1.0) {
  button.on('click', () => {
    if (button['cantClick']) {
      return;
    }
    button['cantClick'] = true;
    cc.tween(button)
      .to(0.1, { scale: scale * 1.1 })
      .to(0.1, { scale: scale })
      .call(() => {
        callback && callback(button);
        button['cantClick'] = false;
      })
      .start();
  });
}

export function guid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
      var r = Math.random() * 16 | 0,
          v = c == 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
  });
}