import { ANNOTATION_TYPE } from '../constants';
import { parseAnnotationObject } from './annotation';

type GetMarkupWithSelectionFunc = ({
  color,
  type,
  opacity,
  scale,
}: {
  color: string;
  type: string;
  opacity: number;
  scale: number;
}) => AnnotationType | null;

export const getMarkupWithSelection: GetMarkupWithSelectionFunc = ({
  color,
  type,
  opacity,
  scale,
}) => {
  const selection: Selection | null = window.getSelection();

  if (!selection || !selection.rangeCount) return null;

  const {
    startContainer,
    startOffset,
    endContainer,
    endOffset,
  } = selection.getRangeAt(0);
  const startElement = startContainer.parentNode as HTMLElement;
  const endElement = endContainer.parentNode as HTMLElement;
  const startPage = startElement?.parentNode?.parentNode as HTMLElement;
  const endPage = endElement?.parentNode?.parentNode as HTMLElement;
  const startPageNum = parseInt(
    startPage.getAttribute('data-page-num') as string,
    10
  );
  const endPageNum = parseInt(
    endPage.getAttribute('data-page-num') as string,
    10
  );
  const textLayer = startPage.querySelector(
    '[data-id="text-layer"]'
  ) as HTMLElement;
  const pageHeight = startPage.offsetHeight;

  if (startPageNum !== endPageNum) return null;
  if (startOffset === endOffset && startOffset === endOffset) return null;

  const startCloneEle = startElement.cloneNode(true) as HTMLElement;
  const endCloneEle = endElement.cloneNode(true) as HTMLElement;

  const startText = startElement.innerText.substring(0, startOffset);
  const endText = endElement.innerText.substring(endOffset);

  startCloneEle.innerText = startText;
  endCloneEle.innerText = endText;
  textLayer.appendChild(startCloneEle);
  textLayer.appendChild(endCloneEle);

  const startExtraWidth = startCloneEle.getBoundingClientRect().width;
  const endExtraWidth = endCloneEle.getBoundingClientRect().width;

  textLayer.removeChild(startCloneEle);
  textLayer.removeChild(endCloneEle);

  const info: AnnotationType = {
    obj_type: '',
    obj_attr: {
      page: 0,
      bdcolor: '',
      position: [],
      transparency: 0,
    },
  };
  const position: PositionType[] = [];

  const startRect = startElement.getBoundingClientRect();
  const endRect = endElement.getBoundingClientRect();

  // left to right and up to down select
  let startX = startElement.offsetLeft + startExtraWidth;
  let startY = startElement.offsetTop;
  let endX = endElement.offsetLeft + endRect.width - endExtraWidth;
  let endY = endElement.offsetTop + endRect.height;

  if (startX > endX && startY >= endY) {
    // right to left and down to up select
    startX = endElement.offsetLeft + startExtraWidth;
    startY = endElement.offsetTop;
    endX = startElement.offsetLeft + startRect.width - endExtraWidth;
    endY = startElement.offsetTop + startRect.height;
  }
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
  // @ts-ignore
  const textElements = [...textLayer.childNodes];

  textElements.forEach((ele: HTMLElement) => {
    const { offsetTop, offsetLeft, offsetHeight, offsetWidth } = ele;
    const offsetRight = offsetLeft + offsetWidth;
    const offsetBottom = offsetTop + offsetHeight;
    const coords = {
      top: offsetTop,
      bottom: offsetBottom,
      left: offsetLeft,
      right: offsetRight,
    };

    if (
      offsetTop >= startY + offsetHeight &&
      offsetBottom <= endY - offsetHeight
    ) {
      position.push(coords);
    } else if (offsetTop >= startY - 2 && offsetBottom <= endY + 2) {
      if (startElement === endElement && startElement === ele) {
        // start and end same element
        coords.left = startX;
        coords.right = endX;
        position.push(coords);
      } else if (startElement === ele) {
        // start element
        coords.left = startX;
        position.push(coords);
      } else if (endElement === ele) {
        // end element
        coords.right = endX;
        position.push(coords);
      } else if (
        (startX < offsetLeft && endX > offsetRight) ||
        (offsetLeft > startX &&
          offsetTop <= startY + 2 &&
          endY > startY + offsetHeight) ||
        (offsetRight < endX &&
          offsetBottom >= endY - 2 &&
          startY < endY - offsetHeight)
      ) {
        // middle element
        position.push(coords);
      }
    }
  });

  for (let i = 1; i < position.length; i += 1) {
    if (
      position[i].top >= position[i - 1].top - 2 &&
      position[i].bottom <= position[i - 1].bottom + 2
    ) {
      position[i - 1].right = position[i].left;
    }
  }

  info.obj_type = ANNOTATION_TYPE[type];
  info.obj_attr = {
    page: startPageNum,
    bdcolor: color,
    position,
    transparency: opacity,
  };

  return parseAnnotationObject(info, pageHeight, scale);
};

export default getMarkupWithSelection;
