import {
  Prefab,
  resources,
  Node,
  find,
  instantiate,
  Vec3,
  log,
  Component,
  Button,
  warn,
  Asset,
  error,
  v2,
  UITransform,
  tween,
  v3,
  Vec2,
  director,
  ImageAsset,
  assetManager,
} from "cc";

import BigNumber from "./BigNumber";

import { EDITOR } from "cc/env";
import { LanguageManager } from "./LanguageManager";
import PoolMgr from "./PoolMgr";
import BaseUI from "../base/BaseUI";
import { Tips } from "../mgr/Tips";

export enum EFLY_TYPE {
  金币,
  体力,
  钻石,
}

export default class Utils {
  public static copyText(text: string) {
    navigator.clipboard.writeText(text).then(
      (res) => {
        console.log(" writeText success: ", res);
        // Tips.show(Utils.setI18nLabel("Share.LinkCopied"));
      },
      (err) => {
        console.log(" writeText error: ", err);
        this.copyTextFallback(text);
      }
    );
  }

  private static copyTextFallback(text) {
    const textArea = document.createElement("textarea");
    textArea.value = text;
    document.body.appendChild(textArea);
    textArea.select();
    try {
      document.execCommand("copy");

      Tips.show(Utils.setI18nLabel("Share.LinkCopied")); //"Link copied!");
    } catch (err) {
      Tips.show(Utils.setI18nLabel("Share.Title1"));
    }
    document.body.removeChild(textArea);
  }

  static sleep(ms: number) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  static padNumber(num, length): string {
    let str = num.toString();
    while (str.length < length) {
      str = "0" + str;
    }
    return str;
  }

  static log(o: any) {
    if ("1" == this.getQueryString("log")) {
      console.log(o);
    }
  }

  // 创建UI
  static createUI(
    filepath: string,
    parent: Node = null,
    callback: Function = null
  ): Promise<Node> {
    return new Promise((resolve, reject) => {
      resources.load(filepath, Prefab, (err, ret) => {
        log("加载UI:" + filepath);
        if (err) {
          console.error(err);
          reject();
          return;
        }

        if (parent == null) {
          parent = find("Canvas/popupNode");
        }

        let index = filepath.lastIndexOf("/");
        let name = filepath.substr(index + 1, filepath.length - index);
        if (parent.getComponentInChildren(name)) {
          console.log("重复UI跳过");
          return;
        }
        var tmp: Node = instantiate(ret);
        tmp.parent = parent;
        if (callback) callback(tmp);
        resolve(tmp);
      });
    });
  }

  // 创建预制体
  static createPrefab(
    filepath: string,
    parent: Node = null,
    callback: Function = null,
    pos: Vec3 = null
  ) {
    return new Promise((resolve, reject) => {
      resources.load(filepath, Prefab, (err, ret) => {
        if (err) {
          console.error(err);
          reject();
          return;
        }

        if (parent == null) {
          parent = find("Canvas");
        }

        var tmp: Node = instantiate(ret);
        // tmp.opacity = 0;
        // tmp.runAction(
        //   sequence(
        //     delayTime(0.01),
        //     callFunc(() => {
        //       tmp.opacity = 255;
        //     })
        //   )
        // );
        tmp.parent = parent;
        if (pos) {
          tmp.position = pos;
        }
        if (callback) callback(tmp);
        resolve(tmp);
      });
    });
  }

  // 获取随机数
  public static getRandom(lower, upper): number {
    return Math.random() * (upper - lower) + lower;
  }

  // 获取随机整数
  public static getRandomInt(lower, upper): number {
    return Math.floor(Math.random() * (upper - lower)) + lower;
  }

  // 获取随机整数
  public static seedRandomInt(lower, upper): number {
    return Utils.getRandomInt(lower, upper);
  }

  // 格式化数字
  public static formatNumber(num: number, afterdot: number = 2): string {
    num = Number(num);
    num = Number(num.toFixed(afterdot));
    if (num < 1000) {
      return num.toString();
    }
    return BigNumber.getLargeString(num);
  }
  public static getPowNum(p) {
    return Math.pow(10, p);
  }

  // 设置服务器时间
  public static setServerTime(time: number) {
    Utils.timeOffset = time - new Date().getTime();
  }

  // 获取服务器时间
  public static timeOffset: number = 0;
  public static getServerTime() {
    return new Date().getTime() + Utils.timeOffset;
  }

  // 添加点击事件
  public static addClickEvent(
    node,
    target,
    component,
    handler,
    customEventData
  ) {
    var eventHandler = new Component.EventHandler();
    eventHandler.target = target;
    eventHandler.component = component;
    eventHandler.handler = handler;
    if (customEventData) eventHandler.customEventData = customEventData;

    var clickEvents = node.getComponent(Button).clickEvents;
    if (clickEvents.length > 0) {
      // if (!EDITOR) warn("按钮已经存在绑定，跳过自动绑定", node.name);
      return;
    }
    console.log(node.name, target.name, component);
    clickEvents.push(eventHandler);
  }

  public static secondsToDHMS(seconds) {
    if (seconds <= 0) {
      return `00d 00h:00m:00s`;
    }
    const days = Math.floor(seconds / (24 * 3600));
    let d = days < 9 ? "0" + days : days;
    const hours = Math.floor((seconds % (24 * 3600)) / 3600);
    let h = hours < 9 ? "0" + hours : hours;
    const minutes = Math.floor((seconds % 3600) / 60);
    let min = minutes < 9 ? "0" + minutes : minutes;
    const second = Math.floor(seconds % 60);
    let s = second < 9 ? "0" + second : second;
    let str = days > 0 ? `${d}d ${h}h:${min}m:${s}s` : `${h}h:${min}m:${s}s`;
    return str;
  }

  public static formatTimestamp(timestamp) {}
  // 秒转换为时分秒
  public static getToTimeByS(second: number) {
    const totalSeconds = Math.floor(second);
    const hours = Math.floor(totalSeconds / 3600) % 24;
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const seconds = totalSeconds % 60;

    return [hours, minutes, seconds]
      .map((unit) => (unit < 10 ? "0" + unit : unit.toString())) // 确保每个部分都是两位数
      .join(":"); // 拼接为字符串
  }

  private static padStartCustom(str, targetLength, padString) {
    str = String(str);
    padString = String(padString);
    if (str.length >= targetLength) {
      return str;
    }
    targetLength -= str.length;
    if (targetLength > padString.length) {
      padString = padString.repeat(Math.ceil(targetLength / padString.length));
    }
    return padString.slice(0, targetLength) + str;
  }

  public static formateDateRemaining(targetTimestamp: number) {
    const now = Utils.getServerTime();

    const remainingTime = targetTimestamp - now;

    const days = Math.floor(remainingTime / (1000 * 60 * 60 * 24));

    const hours = Math.floor(
      (remainingTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
    );

    const minutes = Math.floor(
      (remainingTime % (1000 * 60 * 60)) / (1000 * 60)
    );

    const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000);

    const dStr = days < 10 ? "0" + days : days;
    const hStr = hours < 10 ? "0" + hours : hours;
    const mStr = minutes < 10 ? "0" + minutes : minutes;

    const sStr = seconds < 10 ? "0" + seconds : seconds;

    return `${dStr}d ${hStr}h:${mStr}m:${sStr}s`;
  }

  public static formatTimeRemaining(targetTimestamp) {
    // 获取当前时间戳
    const now = Date.now();

    // 计算剩余时间的毫秒数
    let remainingTime = targetTimestamp - now;

    // 如果剩余时间小于等于 0，表示目标时间已经过去
    if (remainingTime <= 0) {
      return "00:00:00";
    }

    // 计算小时、分钟、秒
    const hours = Math.floor(remainingTime / (1000 * 60 * 60));
    remainingTime -= hours * 1000 * 60 * 60;

    const minutes = Math.floor(remainingTime / (1000 * 60));
    remainingTime -= minutes * 1000 * 60;

    const seconds = Math.floor(remainingTime / 1000);

    // 格式化为两位数的字符串
    const formattedHours = Utils.padStartCustom(hours, 2, "0");
    const formattedMinutes = Utils.padStartCustom(minutes, 2, "0");
    const formattedSeconds = Utils.padStartCustom(seconds, 2, "0");

    return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
  }

  // 秒转换为时分秒
  public static getTimeStrByS(second: number) {
    second = Math.floor(second);
    if (second < 0) second = 0;
    var d = Math.floor(second / 3600 / 24);
    second -= d * 3600 * 24;
    var h = Math.floor(second / 3600);
    second -= h * 3600;
    var m = Math.floor(second / 60);
    second -= m * 60;
    var front = "00";
    if (h > 9) {
      front = "" + h;
    } else {
      front = "0" + h;
    }
    var mid = "00";
    if (m > 9) {
      mid = "" + m;
    } else {
      mid = "0" + m;
    }
    var back = "00";
    if (second > 9) {
      back = "" + second;
    } else {
      back = "0" + second;
    }

    if (d > 0) {
      return d + ":" + h + ":" + m;
    } else {
      var longTime = h > 0;
      if (longTime) {
        return front + ":" + mid;
      } else {
        return mid + ":" + back; //+ '秒';
      }
    }
  }

  public static formattedDate(timestamp) {
    const date = new Date(Number(timestamp));
    // 获取年月日
    const year = date.getFullYear();
    let month = date.getMonth() + 1;
    let monthS = month < 9 ? "0" + month : month;
    let day = date.getDate();
    let dayS = day < 9 ? "0" + day : day;
    const hours = date.getHours();
    let hoursS = hours < 9 ? "0" + hours : hours;
    const minutes = date.getMinutes();
    let minutesS = minutes < 9 ? "0" + minutes : minutes;
    const seconds = date.getSeconds();
    let secondsS = seconds < 9 ? "0" + seconds : seconds;
    const formattedDate = `${year}-${monthS}-${dayS} ${hoursS}:${minutesS}:${secondsS}`;
    return formattedDate;
  }

  public static formattedOnlyDate(timestamp) {
    const date = new Date(Number(timestamp));
    // 获取年月日
    const year = date.getFullYear();
    let month = date.getMonth() + 1;
    let monthS = month < 9 ? "0" + month : month;
    let day = date.getDate();
    let dayS = day < 9 ? "0" + day : day;
  
    const formattedDate = `${year}-${monthS}-${dayS}`;
    return formattedDate;
  }

  // 格式化金币
  public static formatCoin(num: number) {
    num = Math.floor(num);
    return BigNumber.getLargeString(num);
  }

  // 加载资源
  public static loadRes(path: string, type: typeof Asset): Promise<Asset> {
    return new Promise((resolve, reject) => {
      resources.load(path, type, (err, ret) => {
        if (err) {
          error(path, err);
          reject(null);
        } else {
          resolve(ret);
        }
      });
    });
  }

  public static loadRemote(path: string, type: typeof ImageAsset) {
    return new Promise((resolve, reject) => {
      assetManager.loadRemote(path, type, (err, ret) => {
        if (err) {
          error(path, err);
          reject(null);
        } else {
          resolve(ret);
        }
      });
    });
  }

  // 权重
  public static weight(v: number[]): number {
    var mTotalWeight = 0;
    for (var i = 0; i < v.length; ++i) {
      mTotalWeight += v[i];
    }
    if (mTotalWeight <= 0) return -1;
    var randnum = Math.round(Math.random() * Number.MAX_VALUE) % mTotalWeight;
    for (var i = 0; i < v.length; ++i) {
      if (randnum < v[i]) {
        return i;
      } else {
        randnum -= v[i];
      }
    }
    return -1;
  }

  //定点数
  public static fixFloat(val: number, count: number = 2) {
    var a = count * 100;
    return Math.floor(val * a) / a;
  }

  // 在子节点中查找
  private static _findInChildren(node: Node, name: string): Node {
    var x = node.getChildByName(name);
    if (x) return x;
    if (node.children.length == 0) return null;

    for (var i = 0; i < node.children.length; ++i) {
      var tmp = this._findInChildren(node.children[i], name);
      if (tmp) return tmp;
    }
    return null;
  }

  // 飞动动画
  public static flyAnim(
    type: number,
    startNode: Node,
    targetNodeName: string,
    count: number,
    radius: number,
    callback: Function
  ) {
    let srcNode = this._findInChildren(director.getScene(), targetNodeName);
    if (!srcNode) {
      notPlay = false;
      callback();
      return;
    }

    let getPoint = (r, ox, oy, count) => {
      var point = []; //结果
      var radians = (Math.PI / 180) * Math.round(360 / count), //弧度
        i = 0;
      for (; i < count; i++) {
        var x = ox + r * Math.sin(radians * i),
          y = oy + r * Math.cos(radians * i);

        point.unshift(v2(x, y)); //为保持数据顺时针
      }
      return point;
    };

    let createNode = (type) => {
      if (type == 0) return PoolMgr.Instance().get("Coin");
      if (type == 1) return PoolMgr.Instance().get("Gem");
      if (type == 2) return PoolMgr.Instance().get("Zp");
      if (type == 3) return PoolMgr.Instance().get("ZpGold");
    };

    let start = startNode.parent
      .getComponent(UITransform)
      .convertToWorldSpaceAR(startNode.position);
    start = find("Canvas/flyNode")
      .getComponent(UITransform)
      .convertToNodeSpaceAR(start);
    var array = getPoint(radius, start.x, start.y, count);

    var nodeArray = new Array();
    for (var i = 0; i < array.length; i++) {
      var gold = createNode(type);
      gold.parent = find("Canvas/flyNode");
      var randPos = v2(
        array[i].x + Utils.getRandomInt(0, 50),
        array[i].y + Utils.getRandomInt(0, 50)
      );
      gold.setPosition(start);
      nodeArray.push({ gold, randPos });
    }

    var notPlay = false;

    let dstPos = srcNode.parent
      .getComponent(UITransform)
      .convertToWorldSpaceAR(srcNode.position);
    dstPos = find("Canvas/flyNode")
      .getComponent(UITransform)
      .convertToNodeSpaceAR(dstPos);

    var targetGoldNode = srcNode;
    for (var i = 0; i < nodeArray.length; i++) {
      var pos = nodeArray[i].randPos;
      var node = nodeArray[i].gold;
      nodeArray[i].gold.id = i;

      tween(node)
        .to(0.2, { position: pos })
        .delay(i * 0.03)
        .to(0.5, { position: v2(dstPos.x, dstPos.y) })
        .call(() => {
          if (!notPlay) {
            targetGoldNode.scale = v3(1, 1, 1);
            notPlay = true;

            tween(targetGoldNode)
              .to(0.1, { scale: v3(2, 2, 2) })
              .to(0.1, { scale: v3(1, 1, 1) })
              .call(() => {
                notPlay = false;
              });
          }
          callback(node._id == nodeArray.length - 1);
          PoolMgr.Instance().put(node.name, node);
          if (node._id == nodeArray.length - 1) {
            find("Canvas/flyNode").removeAllChildren();
          }
        })
        .start();
    }
  }

  /**
   * 深度复制
   * @param value
   */
  public static deepClone(value: any) {
    // 处理基本数据类型和特殊情况
    if (value === null || typeof value !== "object") {
      return value; // 基本数据类型和 null
    }

    // 处理数组
    if (Array.isArray(value)) {
      return value.map((item) => Utils.deepClone(item)); // 递归拷贝每个元素
    }

    // 处理对象
    const copy = {};
    for (const key in value) {
      if (value.hasOwnProperty(key)) {
        copy[key] = Utils.deepClone(value[key]); // 递归拷贝每个属性
      }
    }

    return copy;
  }

  /**
   * 获取url参数
   * @param name
   * @returns
   */
  public static getQueryString(name: any) {
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
    var r = window.location.search.substr(1).match(reg);
    if (r != null) return unescape(r[2]);
    return null;
  }

  /**
   * 贝塞尔曲线计算
   * @param points
   * @param t
   */
  public static calculateBezier(points: Vec2[], t: number) {
    const n = points.length;

    if (n === 0) return v2(0, 0); // 如果没有控制点，返回 (0,0)
    if (n === 1) return points[0]; // 如果只有一个控制点，返回该点

    let tempPoints = points.slice(); // 创建控制点的副本

    // De Casteljau's algorithm
    for (let r = 1; r < n; r++) {
      for (let i = 0; i < n - r; i++) {
        tempPoints[i] = v2(
          (1 - t) * tempPoints[i].x + t * tempPoints[i + 1].x,
          (1 - t) * tempPoints[i].y + t * tempPoints[i + 1].y
        );
      }
    }

    return tempPoints[0]; // 返回计算得到的点
  }

  /**
   * 是否为某个链接
   * @param url
   * @param hostname
   * @returns
   */
  static isNameLink(url: string | URL, hostname: string) {
    try {
      const parsedUrl = new URL(url);
      return parsedUrl.hostname === hostname;
    } catch (e) {
      console.error("Invalid URL");
      return false;
    }
  }

  // 将用户 ID 转换为 26 进制字符串
  public static convertTo26(userId) {
    let sb = "";
    while (userId > 0) {
      userId--;
      let ch = String.fromCharCode((userId % 26) + "A".charCodeAt(0));
      sb = ch + sb;
      userId = Math.floor(userId / 26);
    }
    return sb;
  }

  // 生成邀请代码
  public static generateInviteCode(channelId, userId) {
    return channelId + this.convertTo26(userId);
  }

  // 取后缀
  public static getSuffix(idx) {
    idx = Math.floor(idx);
    if (idx <= 0) {
      return "";
    }
    idx = idx - 1;
    let result = "";
    while (idx >= 0) {
      result = String.fromCharCode((idx % 26) + 97) + result;
      idx = Math.floor(idx / 26) - 1;
    }
    result = "";
    return result;
  }

  // max: 10 ==> 1->1,2->2,..10->10,11->1
  public static getMappedValue(idx, max) {
    let result;
    result = idx % max == 0 ? max : idx % max;
    return result;
  }

  //
  public static getStarValue(idx, max) {
    let result = Math.floor((idx - 1) / max);
    return result;
  }

  // 格式化地址
  static formAddress(address: string) {
    if (
      address == null ||
      address == "" ||
      address == undefined ||
      address.length < 10
    ) {
      return " ";
    }
    return (
      address.substring(0, 8) +
      "..." +
      address.substring(address.length - 8, address.length)
    );
  }

  //
  public static updateLabelText(fullText, maxWidth, fontSize) {
    let str = "";
    // 计算文本的实际宽度
    let textWidth = this.getTextWidth(fullText, fontSize);

    if (textWidth > maxWidth) {
      // 超过最大宽度，显示截断后的文本并加上省略号
      let truncatedText = this.truncateText(fullText, maxWidth, fontSize);
      str = truncatedText + "...";
    } else {
      // 显示完整文本
      str = fullText;
    }
    return str;
  }

  // 获取文本的宽度
  private static getTextWidth(text, fontSize) {
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");
    ctx.font = fontSize + "px Arial"; // 设置字体大小
    return ctx.measureText(text).width;
  }

  // 截断文本并返回适合的文本
  private static truncateText(text, maxWidth, fontSize) {
    let i = 0;
    let truncatedText = text;
    // 循环截取字符直到文本超出最大宽度
    while (
      this.getTextWidth(truncatedText, fontSize) > maxWidth &&
      i < text.length
    ) {
      i++;
      truncatedText = text.slice(0, text.length - i);
    }
    return truncatedText;
  }

  // 多语言
  static setI18nLabel(str: string, value?: string, value2?: string) {
    let i18nStr = str;
    i18nStr = LanguageManager.getText(str);
    if (value) {
      i18nStr = i18nStr.replace("{value}", value);
    }
    if (value2) {
      i18nStr = i18nStr.replace("{value2}", value2);
    }
    return i18nStr;
  }
}
