import colorMap from "./color";

export const AnnotationType = {
  TEXT: 1,
  LINK: 2,
  FREETEXT: 3,
  LINE: 4,
  SQUARE: 5,
  CIRCLE: 6,
  POLYGON: 7,
  POLYLINE: 8,
  HIGHLIGHT: 9,
  UNDERLINE: 10,
  SQUIGGLY: 11,
  STRIKEOUT: 12,
  STAMP: 13,
  CARET: 14,
  INK: 15,
  POPUP: 16,
  FILEATTACHMENT: 17,
  SOUND: 18,
  MOVIE: 19,
  WIDGET: 20,
  SCREEN: 21,
  PRINTERMARK: 22,
  TRAPNET: 23,
  WATERMARK: 24,
  THREED: 25,
  REDACT: 26,
};
export class IdManager {
  _id = 0;
  getId(page) {
    return `annot_${page}_${this._id++}`;
  }
}
function getModificationDate(date = new Date()) {
  const buffer = [
    date.getUTCFullYear().toString(),
    (date.getUTCMonth() + 1).toString().padStart(2, "0"),
    date.getUTCDate().toString().padStart(2, "0"),
    date.getUTCHours().toString().padStart(2, "0"),
    date.getUTCMinutes().toString().padStart(2, "0"),
    date.getUTCSeconds().toString().padStart(2, "0"),
  ];

  return buffer.join("");
}
function findCommonSubstring(text1, text2) {
  const m = text1.length;
  const n = text2.length;
  const dp = Array.from(Array(m + 1), () => Array(n + 1).fill(0));
  let maxLength = 0;
  let endIndex = 0;

  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (text1[i - 1] === text2[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
        if (dp[i][j] > maxLength) {
          maxLength = dp[i][j];
          endIndex = i - 1;
        }
      }
    }
  }

  if (maxLength === 0) {
    return "";
  }

  return text1.substring(endIndex - maxLength + 1, endIndex + 1);
}

export class Annotation {
  #data = {};
  constructor(param) {
    this.#data = {
      annotationFlags: 4,
      annotationType: this.getType(param.subtype),
      borderStyle: {
        dashArray: [3],
        horizontalCornerRadius: 0,
        style: 1,
        verticalCornerRadius: 0,
        width: 0,
      },
      color: new Uint8ClampedArray(param.color),
      backgroundColor: null,
      borderColor: null,
      rotation: 0,
      contentsObj: {
        str: "",
        dir: "ltr",
      },
      hasAppearance: false,
      id: param.id,
      modificationDate: `D:${getModificationDate()}`,
      rect: param.rect,
      quadPoints: param.quadPoints,
      creationDate: `D:${getModificationDate()}`,
      hasOwnCanvas: false,
      subtype: param.subtype,
      noRotate: false,
      noHTML: false,
      titleObj: { str: "author", dir: "ltr" },
    };
  }
  getType(type) {
    const p = type.toLocaleUpperCase();
    return AnnotationType[p];
  }
  getData() {
    return this.#data;
  }
}

const InvisibleCharactersRegExp = /[\x01-\x1F]/g;

function removeNullCharacters(str, replaceInvisible = false) {
  if (typeof str !== "string") {
    console.error(`The argument must be a string.`);
    return str;
  }
  if (replaceInvisible) {
    str = str.replaceAll(InvisibleCharactersRegExp, " ");
  }
  return str.replaceAll("\x00", "");
}
export class PdfAnnotation {
  #pdfViewer = null;
  #pdfJs = null;
  #idManager = null;

  _annotations = [];
  constructor(param) {
    this.#pdfViewer = param.pdfViewer;
    this.#pdfJs = param.pdfJs;

    this.#idManager = new IdManager();
  }
  getPageAnno() {
    // 获取当前选择的所有页
    const pageIndexs = this.getPageIndexs();

    let carr = []; // 每页的行数组 [页:[行],页:[行]]
    if (pageIndexs.length > 0) {
      // 获取页数据判断分页的行数据
      const pageRect = this.getPageView();
      const rects = window.getSelection()?.getRangeAt(0).getClientRects();

      const pagesRow = [];
      // 将数据划分到每页
      if (rects) {
        let index = 0;
        for (const o of Array.from(rects)) {
          // 宽度与页面宽度相等为分页行
          // @ts-ignore
          if (o.width === pageRect.width) {
            index += 1;
            continue;
          }
          // 将数据放到对应的页数组中
          pagesRow[index] ? pagesRow[index].push(o) : (pagesRow[index] = [o]);
        }
      }
      // 清除空元素
      let _arr = [...pagesRow].filter((o) => o);

      carr = _arr.map((u) => {
        // 重新构建分配好的行数据
        const newRow = [];
        let Index = 0;

        for (const [ind, o] of u.entries()) {
          // 第一个数据不做判断
          if (ind === 0) {
            newRow[Index] = [o];
          } else if (o.width === 0) {
            // width为0为换行
            Index += 1;
          } else if (u[ind - 1].width === 0) {
            // 对于存在width为0的页行数据处理
            // 换行行的后一行数据直接添加
            newRow[Index] = [o];
          } else if (ind > 0) {
            // 对于没有明显的换行行（即width===0）的处理
            // 判断当前行与前一行的行间距
            // 行间距大于阈值的换行到下一行
            // 不同的行间距基本大于10
            const L = Math.abs(o.top - u[ind - 1].top);
            if (L > 9) {
              Index += 1;
              newRow[Index] = [o];
            } else {
              newRow[Index].push(o);
            }
          }
        }
        return newRow;
      });
    }
    // 整页行划分
    const p = {};
    // 遍历页

    for (const [ind, t] of carr.entries()) {
      // 遍历页的行
      const da = {
        rect: [],
        quadPoints: [],
      };
      da.rect = this.getRect(+pageIndexs[ind], t);
      for (const s of t) {
        const r = this.geintegrationRect(s);

        if (r) {
          const re = this.getPointFromRect(+pageIndexs[ind], r);
          da.quadPoints.push(this.getQuadPoints(re));
        }
      }
      p[pageIndexs[ind]] = da;
    }

    return p;
  }
  getAnnoations() {
    return this._annotations;
  }

  addAnnots(annots) {
    if (Array.isArray(annots)) {
      const s = {};

      for (const t of annots) {
        if (!s[t.page]) {
          s[t.page] = [];
        }
        s[t.page].push(t);
      }

      const promises = Object.keys(s).map((t) => this.#pdfJs.getPage(+t + 1));
      Promise.all(promises).then((pages) => {
        for (const page of pages) {
          page.rAddAnnotation(s[page._pageIndex]);
          this.#pdfViewer._pages[page._pageIndex].updateAnnotationLayer(
            s[page._pageIndex]
          );
        }
      });
      this._annotations = [...this._annotations, ...annots];
    }
  }

  setAnno(param) {
    return new Promise((resolve) => {
      const annos = this.getPageAnno();

      // 获取页对象
      const promises = Object.keys(annos).map((i) => this.#pdfJs.getPage(+i));
      const str = document.getSelection()?.toString();
      Promise.all(promises)
        .then(async (pages) => {
          const annots = [];
          for (const page of pages) {
            const ind = page._pageIndex + 1;
            const rec = annos[ind];
            let Anno = new Annotation({
              subtype: param.subtype,
              id: this.#idManager.getId(ind),
              rect: rec.rect,
              quadPoints: rec.quadPoints,
              color: convertColorToRGB(param.color),
            }).getData();
            Anno = {
              ...Anno,
              page: page._pageIndex,
            };
            const texts = [];
            const buffer = [];
            const { items } = await page.getTextContent();
            for (const item of items) {
              if (item.str) {
                buffer.push(item.str);
              }
              if (item.hasEOL) {
                buffer.push("\n");
              }
            }
            texts.push(removeNullCharacters(buffer.join("")));
            let a = texts.join("\n");
            let b = str?.replaceAll("ﬁ", "fi").replace("ﬂ", "fl") || "";
            // 行数据处理

            let f = findCommonSubstring(a, b);
            // @ts-ignore
            Anno.info = f;
            annots.push(Anno);
            page.addAnnotation(Anno);
            this.#pdfViewer._pages[page._pageIndex].updateAnnotationLayer(Anno);
          }
          this._annotations = [...this._annotations, ...annots];
        })
        .finally(() => {
          document.getSelection()?.empty();
          resolve(this._annotations);
        });
    });
  }
  note(param) {
    return new Promise((resolve) => {
      const annos = this.getPageAnno();

      // 获取页对象
      const promises = Object.keys(annos).map((i) => this.#pdfJs.getPage(+i));
      const str = document.getSelection()?.toString();
      Promise.all(promises).then(async (pages) => {
        const annots = [];
        for (const page of pages) {
          const ind = page._pageIndex + 1;
          const rec = annos[ind];
          let Anno = new Annotation({
            subtype: param.subtype,
            id: this.#idManager.getId(ind),
            rect: rec.rect,
            quadPoints: rec.quadPoints,
            color: convertColorToRGB(param.color),
          }).getData();
          Anno = {
            ...Anno,
            page: page._pageIndex,
            color: convertColorToRGB(param.color),
          };
          const texts = [];
          const buffer = [];
          const { items } = await page.getTextContent();
          for (const item of items) {
            if (item.str) {
              buffer.push(item.str);
            }
            if (item.hasEOL) {
              buffer.push("\n");
            }
          }
          texts.push(removeNullCharacters(buffer.join("")));
          let a = texts.join("\n");
          let b = str?.replaceAll("ﬁ", "fi").replace("ﬂ", "fl") || "";
          // 行数据处理

          let f = findCommonSubstring(a, b);
          // @ts-ignore
          Anno.info = f;
          annots.push(Anno)
        }
        resolve(annots);
      });
    });
  }

  getPageIndexs() {
    const r = window.getSelection()?.getRangeAt(0).commonAncestorContainer;
    let pages = [];

    // 页面获取
    if (r?.nodeType === 1) {
      pages = [];
      // 当前页
      if (r.classList.contains("textLayer")) {
        pages.push(r.parentNode?.dataset.pageNumber);
      } else {
        // 跨页
        const nodes =
          window.getSelection()?.getRangeAt(0).cloneContents().childNodes || [];
        nodes.forEach((element) => {
          pages.push(element?.dataset.pageNumber);
        });
      }
    } else if (r?.nodeType === 3) {
      // 单行

      const t = this.findElementsWithClass(r.parentNode, "page");

      if (t) {
        pages = [t.dataset?.pageNumber];
      }
    }

    return pages;
  }
  findElementsWithClass(node, className) {
    // 如果当前节点为空，则返回一个空数组
    if (!node) {
      return null;
    }

    // 收集当前节点下的所有包含指定类的子节点
    let elements = null;

    if (node.classList.contains(className)) {
      return node;
    }

    // 对当前节点的父节点进行递归查找，并将结果合并到elements数组中
    let parent = node.parentNode;
    if (parent) {
      elements = this.findElementsWithClass(parent, className);
    }

    return elements;
  }
  normalizeRect(rect) {
    const r = rect.slice(0); // clone rect
    if (rect[0] > rect[2]) {
      r[0] = rect[2];
      r[2] = rect[0];
    }
    if (rect[1] > rect[3]) {
      r[1] = rect[3];
      r[3] = rect[1];
    }
    return r;
  }
  getRect(page, rects) {
    const p = rects.flat(Infinity);
    const rect = this.geintegrationRect(p);
    let a = [];
    if (rect) {
      a = this.getPointFromRect(page, rect);
    }
    return a;
  }

  /**
   * @name 获取行数据的最大最小坐标
   * @param rect 行坐标对象
   */
  geintegrationRect(rects) {
    if (rects.length === 0) {
      return null;
    }
    if (rects.length === 1) {
      return {
        left: rects[0].left,
        top: rects[0].top,
        right: rects[0].right,
        bottom: rects[0].bottom,
      };
    }

    let minX = rects[0].left,
      minY = rects[0].top,
      maxX = rects[0].right,
      maxY = rects[0].bottom;
    // 遍历一行中所有的区域块，获取最大最小X
    for (const t of rects) {
      if (t.left < minX) {
        minX = t.left;
      }
      if (t.right > maxX) {
        maxX = t.right;
      }
      if (t.top < minY) {
        minY = t.top;
      }
      if (t.bottom > maxY) {
        maxY = t.bottom;
      }
    }

    return {
      left: minX,
      top: minY,
      right: maxX,
      bottom: maxY,
    };
  }

  /**
   * @name 当前行相对于当前页面的坐标
   * @param page [number]
   * @param rect 行矩阵对象
   */
  getPointFromRect(page, rect) {
    let pg = document.querySelector(`.page[data-page-number='${page}']`);
    var pageRect = pg?.getBoundingClientRect();
    if (rect && pageRect) {
      const x = rect.left - pageRect.left;
      const y = Math.abs(pageRect.top - rect.top);
      const x1 = rect.right - pageRect.left;
      const y1 = Math.abs(pageRect.top - rect.bottom);
      // 当前行相对于当前页面的坐标

      const [_x1, _y1] = this.getViewPort(page, x, y);
      const [_x2, _y2] = this.getViewPort(page, x1, y1);
      const Coord = this.normalizeRect([_x1, _y1, _x2, _y2]);
      return Coord;
    }
    return [];
  }
  /**
   * @name 获取视口坐标
   * @param page 页面
   * @param x 要转换的坐标X
   * @param y 要转换的坐标Y
   */
  getViewPort(page, x, y) {
    let x_1_y_1 = this.#pdfViewer?._pages[page - 1].viewport.convertToPdfPoint(
      x,
      y
    );

    return x_1_y_1;
  }

  getQuadPoints(Coord) {
    return [
      {
        x: Coord[0] + 0.5,
        y: Coord[3] - 2,
      },
      {
        x: Coord[2] - 0.5,
        y: Coord[3] - 2,
      },
      {
        x: Coord[0] + 0.5,
        y: Coord[1] + 1.5,
      },
      {
        x: Coord[2] - 0.5,
        y: Coord[1] + 1.5,
      },
    ];
  }

  getPageView() {
    let pg = document.querySelector(`.page[data-page-number='${1}']`);
    var rect = pg?.getBoundingClientRect();
    return rect;
  }
}

export function convertColorToRGB(color) {
  // 如果颜色已经是 RGB 格式，则直接返回该颜色
  if (Array.isArray(color) && color.length === 3) {
    return color;
  }

  // 将颜色转换为 RGB 格式
  let rgbColor = [0, 0, 0];

  if (typeof color === "string") {
    // 如果颜色是十六进制格式，则将其转换为 RGB 格式
    if (color.startsWith("#")) {
      const hexColor = color.slice(1);
      const r = parseInt(hexColor.substr(0, 2), 16);
      const g = parseInt(hexColor.substr(2, 2), 16);
      const b = parseInt(hexColor.substr(4, 2), 16);
      rgbColor = [r, g, b];
    }
    // 如果颜色是 CSS 颜色关键字，则将其转换为 RGB 格式
    else if (colorMap.has(color)) {
      rgbColor = colorMap.get(color);
    }
  }
  // 如果颜色是 RGB 格式，则将其转换为 `[R, G, B]` 格式
  else if (Array.isArray(color)) {
    rgbColor = color;
  }

  return rgbColor;
}
