import { store } from "@/store/store";
import { PictureSlice, Task } from "@/types";

// 打开cache后，会造成没有 vue devtools 时，canvas缓存错误，暂时不知道原因
// 通过回看的测试，打开回看，再关闭回看，稍等一会儿再打开回看，确实可以看到该缓存时缓存了，该丢弃时丢弃了

// 把store.currentTask当做 weakRef ，当它不存在时，就丢弃它所有的图片
// const weakedMapImages = new WeakMap<Object, Map<string, HTMLImageElement>>();

/**
 * 异步获取图片
 * @param url 完整的图片路径
 * @returns Promise<HTMLImageElement>
 */
export async function loadImage(url: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.setAttribute("crossorigin", "anonymous");
    image.src = url;
    image.onload = () => {
      resolve(image);
    };
    image.onerror = () => {
      reject("图片载入错误");
    };
  });
}

// 存放裁切图的ObjectUrls
let objectUrlMap = new Map<string, string>();
const OBJECT_URLS_MAP_MAX_SIZE =
  window.APP_OPTIONS?.OBJECT_URLS_MAP_MAX_SIZE ?? 100;

export async function getDataUrlForSliceConfig(
  image: HTMLImageElement,
  sliceConfig: PictureSlice,
  maxSliceWidth: number,
  urlForCache: string
) {
  const { i, x, y, w, h } = sliceConfig;
  const key = `${urlForCache}-${i}-${x}-${y}-${w}-${h}`;

  if (objectUrlMap.get(key)) {
    console.log("cached slice objectUrl");
    return objectUrlMap.get(key);
  }

  const canvas = document.createElement("canvas");
  canvas.width = Math.max(sliceConfig.w, maxSliceWidth);
  canvas.height = sliceConfig.h;
  const ctx = canvas.getContext("2d");
  if (!ctx) {
    console.log('canvas.getContext("2d") error');
    throw new Error("canvas ctx error");
  }
  // drawImage 画图软件透明色
  ctx.drawImage(
    image,
    sliceConfig.x,
    sliceConfig.y,
    sliceConfig.w,
    sliceConfig.h,
    0,
    0,
    sliceConfig.w,
    sliceConfig.h
  );
  // console.log(image, canvas.height, sliceConfig, ctx);
  // console.log(canvas.toDataURL());

  // 如果用toBlob，则产生异步，而且URL.createObjectURL还会需要手动释放
  // const dataurl = canvas.toDataURL();
  const blob: Blob = await new Promise((res) => {
    canvas.toBlob((b) => res(b));
  });
  const dataurl = URL.createObjectURL(blob);

  cacheFIFO();

  objectUrlMap.set(key, dataurl);

  return dataurl;
}

// 清理缓存的过时数据(清除头10张)，First in first out
function cacheFIFO() {
  if (objectUrlMap.size > OBJECT_URLS_MAP_MAX_SIZE) {
    const ary = [...objectUrlMap.entries()];
    const toRelease = ary.splice(0, 10);
    // 为了避免部分图片还没显示就被revoke了，这里做一个延迟revoke
    // 此处有个瑕疵，缩略图的显示与试卷不是同时显示，是有可能被清除了的，只能让用户刷新了。 => 见下面的fix
    for (const u of toRelease) {
      // 如果当前图片仍在引用 objectUrl , 则将其放入缓存中
      if (document.querySelector(`img[src="${u[1]}"]`)) {
        ary.push(u);
      } else {
        // console.log("revoke ", u[1]);
        URL.revokeObjectURL(u[1]);
      }
    }
    objectUrlMap = new Map(ary);
  }
}

export async function getDataUrlForSplitConfig(
  image: HTMLImageElement,
  config: [number, number],
  maxSliceWidth: number,
  urlForCache: string
) {
  const [start, end] = config;
  const key = `${urlForCache}-${start}-${end}`;

  if (objectUrlMap.get(key)) {
    console.log("cached split objectUrl");
    return objectUrlMap.get(key);
  }

  const width = image.naturalWidth * (end - start);
  const canvas = document.createElement("canvas");
  canvas.width = Math.max(width, maxSliceWidth);
  canvas.height = image.naturalHeight;
  const ctx = canvas.getContext("2d");
  if (!ctx) {
    console.log('canvas.getContext("2d") error');
    throw new Error("canvas ctx error");
  }
  // drawImage 画图软件透明色
  ctx.drawImage(
    image,
    image.naturalWidth * start,
    0,
    image.naturalWidth * end,
    image.naturalHeight,
    0,
    0,
    image.naturalWidth * end,
    image.naturalHeight
  );

  // 如果用toBlob，则产生异步，而且URL.createObjectURL还会需要手动释放
  // const dataurl = canvas.toDataURL();
  const blob: Blob = await new Promise((res) => {
    canvas.toBlob((b) => res(b));
  });
  const dataurl = URL.createObjectURL(blob);
  cacheFIFO();

  objectUrlMap.set(key, dataurl);
  return dataurl;
}

export async function getDataUrlForCoverConfig(
  image: HTMLImageElement,
  configs: PictureSlice[]
) {
  const key = `${image.src}`;
  if (objectUrlMap.get(key)) {
    return objectUrlMap.get(key);
  }

  const canvas = document.createElement("canvas");
  canvas.width = image.naturalWidth;
  canvas.height = image.naturalHeight;
  const ctx = canvas.getContext("2d");
  if (!ctx) {
    console.log('canvas.getContext("2d") error');
    throw new Error("canvas ctx error");
  }
  ctx.drawImage(image, 0, 0);
  ctx.fillStyle = "#ffffff";
  configs.forEach((config) => {
    ctx.fillRect(config.x, config.y, config.w, config.h);
  });

  const blob: Blob = await new Promise((res) => {
    canvas.toBlob((b) => res(b));
  });
  const dataurl = URL.createObjectURL(blob);
  cacheFIFO();
  objectUrlMap.set(key, dataurl);
  return dataurl;
}

export async function preDrawImage(_currentTask: Task | undefined) {
  // console.log("preDrawImage=>curTask:", _currentTask);

  if (!_currentTask?.taskId) return;

  let maxSliceWidth = 0; // 最大的裁切块宽度，图片容器以此为准

  // const hasSliceConfig = store.currentTask?.sliceConfig?.length;
  const hasSliceConfig = _currentTask?.sliceConfig?.length;

  const images = [];

  if (hasSliceConfig) {
    // 必须要先加载一遍，把“选择整图”的宽高重置后，再算总高度
    const sliceNum = _currentTask.sliceUrls.length;
    if (_currentTask.sliceConfig.some((v) => v.i > sliceNum)) {
      console.warn("裁切图设置的数量小于该学生的总图片数量");
    }
    _currentTask.sliceConfig = _currentTask.sliceConfig.filter(
      (v) => v.i <= sliceNum
    );
    for (const sliceConfig of _currentTask.sliceConfig) {
      const url = _currentTask.sliceUrls[sliceConfig.i - 1];
      const image = await loadImage(url);
      images[sliceConfig.i] = image;
      const { x, y, w, h } = sliceConfig;
      x < 0 && (sliceConfig.x = 0);
      y < 0 && (sliceConfig.y = 0);
      if (sliceConfig.w === 0 && sliceConfig.h === 0) {
        // 选择整图时，w/h 为0
        sliceConfig.w = image.naturalWidth;
        sliceConfig.h = image.naturalHeight;
      }
      if (x <= 1 && y <= 1 && sliceConfig.w <= 1 && sliceConfig.h <= 1) {
        sliceConfig.x = image.naturalWidth * x;
        sliceConfig.y = image.naturalHeight * y;
        sliceConfig.w = image.naturalWidth * w;
        sliceConfig.h = image.naturalHeight * h;
      }
    }

    maxSliceWidth = Math.max(..._currentTask.sliceConfig.map((v) => v.w));
    // 用来保存sliceImage在整个图片容器中(不包括image-seperator)的高度范围
    for (const sliceConfig of _currentTask.sliceConfig) {
      const url = _currentTask.sliceUrls[sliceConfig.i - 1];
      const image = images[sliceConfig.i];

      try {
        await getDataUrlForSliceConfig(image, sliceConfig, maxSliceWidth, url);
      } catch (error) {
        console.log("preDrawImage failed: ", error);
      }
    }
  } else {
    for (const url of _currentTask.sliceUrls) {
      const image = await loadImage(url);
      images.push(image);
    }

    const splitConfigPairs = store.setting.splitConfig.reduce<
      [number, number][]
    >((a, v, index) => {
      index % 2 === 0 ? a.push([v, -1]) : (a.at(-1)![1] = v);
      return a;
    }, []);

    const maxSplitConfig = Math.max(...store.setting.splitConfig);
    maxSliceWidth =
      Math.max(...images.map((v) => v.naturalWidth)) * maxSplitConfig;

    for (const url of _currentTask.sliceUrls) {
      for (const config of splitConfigPairs) {
        const indexInSliceUrls = _currentTask.sliceUrls.indexOf(url) + 1;
        const image = images[indexInSliceUrls - 1];

        try {
          await getDataUrlForSplitConfig(image, config, maxSliceWidth, url);
        } catch (error) {
          console.log("preDrawImage failed: ", error);
        }
      }
    }
  }
}

export async function processSliceUrls(_currentTask: Task | undefined) {
  if (!_currentTask?.taskId) return;

  const getNum = (num) => Math.max(Math.min(1, num), 0);

  const sheetUrls = _currentTask.sheetUrls || [];
  const sheetConfig = (store.setting.sheetConfig || []).map((item) => {
    return { ...item };
  });

  const urls = [];
  for (let i = 0; i < sheetUrls.length; i++) {
    const url = sheetUrls[i];
    const configs = sheetConfig.filter((item) => item.i === i + 1);
    if (!configs.length) {
      urls[i] = url;
      continue;
    }
    const image = await loadImage(url);
    configs.forEach((item) => {
      item.x = image.naturalWidth * getNum(item.x);
      item.y = image.naturalHeight * getNum(item.y);
      item.w = image.naturalWidth * getNum(item.w);
      item.h = image.naturalHeight * getNum(item.h);
    });

    urls[i] = await getDataUrlForCoverConfig(image, configs);
  }
  return urls;
}

export async function preDrawImageHistory(_currentTask: Task | undefined) {
  console.log("preDrawImageHistory=>curTask:", _currentTask);

  if (!_currentTask?.taskId) return;

  let maxSliceWidth = 0; // 最大的裁切块宽度，图片容器以此为准

  // const hasSliceConfig = store.currentTask?.sliceConfig?.length;
  const hasSliceConfig = _currentTask?.sliceConfig?.length;
  // _currentTask.sheetUrls = ["/1-1.jpg", "/1-2.jpg"];
  _currentTask.sliceUrls = await processSliceUrls(_currentTask);

  const images = [];

  if (hasSliceConfig) {
    // 必须要先加载一遍，把“选择整图”的宽高重置后，再算总高度
    const sliceNum = _currentTask.sliceUrls.length;
    if (_currentTask.sliceConfig.some((v) => v.i > sliceNum)) {
      console.warn("裁切图设置的数量小于该学生的总图片数量");
    }
    _currentTask.sliceConfig = _currentTask.sliceConfig.filter(
      (v) => v.i <= sliceNum
    );
    for (const sliceConfig of _currentTask.sliceConfig) {
      const url = _currentTask.sliceUrls[sliceConfig.i - 1];
      const image = await loadImage(url);
      images[sliceConfig.i] = image;
      const { x, y, w, h } = sliceConfig;
      x < 0 && (sliceConfig.x = 0);
      y < 0 && (sliceConfig.y = 0);
      if (sliceConfig.w === 0 && sliceConfig.h === 0) {
        // 选择整图时，w/h 为0
        sliceConfig.w = image.naturalWidth;
        sliceConfig.h = image.naturalHeight;
      }
      if (x <= 1 && y <= 1 && sliceConfig.w <= 1 && sliceConfig.h <= 1) {
        sliceConfig.x = image.naturalWidth * x;
        sliceConfig.y = image.naturalHeight * y;
        sliceConfig.w = image.naturalWidth * w;
        sliceConfig.h = image.naturalHeight * h;
      }
    }

    maxSliceWidth = Math.max(..._currentTask.sliceConfig.map((v) => v.w));

    // 用来保存sliceImage在整个图片容器中(不包括image-seperator)的高度范围
    for (const sliceConfig of _currentTask.sliceConfig) {
      const url = _currentTask.sliceUrls[sliceConfig.i - 1];
      const image = images[sliceConfig.i];

      try {
        await getDataUrlForSliceConfig(image, sliceConfig, maxSliceWidth, url);
      } catch (error) {
        console.log("preDrawImage failed: ", error);
      }
    }
  } else {
    for (const url of _currentTask.sliceUrls) {
      const image = await loadImage(url);
      images.push(image);
    }

    const splitConfigPairs = store.setting.splitConfig.reduce<
      [number, number][]
    >((a, v, index) => {
      index % 2 === 0 ? a.push([v, -1]) : (a.at(-1)![1] = v);
      return a;
    }, []);

    const maxSplitConfig = Math.max(...store.setting.splitConfig);
    maxSliceWidth =
      Math.max(...images.map((v) => v.naturalWidth)) * maxSplitConfig;

    for (const url of _currentTask.sliceUrls) {
      for (const config of splitConfigPairs) {
        const indexInSliceUrls = _currentTask.sliceUrls.indexOf(url) + 1;
        const image = images[indexInSliceUrls - 1];

        try {
          await getDataUrlForSplitConfig(image, config, maxSliceWidth, url);
        } catch (error) {
          console.log("preDrawImage failed: ", error);
        }
      }
    }
  }
}

export function addFileServerPrefixToTask(rawTask: Task): Task {
  const newTask = JSON.parse(JSON.stringify(rawTask)) as Task;
  return newTask;
}

export function addHeaderTrackColorAttr(headerTrack: any): any {
  return headerTrack.map((item: any) => {
    item.color = "green";
    return item;
  });
}

/**
 * 获取随机code,默认获取16位
 * @param {Number} len 推荐8的倍数
 *
 */
export function randomCode(len = 16): string {
  if (len <= 0) return;
  const steps = Math.ceil(len / 8);
  const stepNums = [];
  for (let i = 0; i < steps; i++) {
    const ranNum = Math.random().toString(32).slice(-8);
    stepNums.push(ranNum);
  }

  return stepNums.join("");
}

/**
 * 解析url获取指定参数值
 * @param urlStr url
 * @param paramName 参数名
 * @returns 参数值
 */
export function parseHrefParam(
  urlStr: string,
  paramName: string = null
): string | object {
  if (!urlStr) return;
  const url = new URL(urlStr);

  const urlParams = new URLSearchParams(url.search);

  if (paramName) return urlParams.get(paramName);

  const params = {};
  for (const kv of urlParams.entries()) {
    params[kv[0]] = kv[1];
  }
  return params;
}

/**
 * 计算总数
 * @param {Array} dataList 需要统计的数组
 */
export function calcSum(dataList) {
  if (!dataList.length) return 0;
  return dataList.reduce(function (total, item) {
    return total + item;
  }, 0);
}
