/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-underscore-dangle */
import { fromEvent } from 'rxjs';
import { debounceTime, throttleTime } from 'rxjs/operators';

export const objIsEmpty = (obj: Record<string, any>): boolean =>
  !Object.keys(obj).length;

export const watchScroll = (
  viewAreaElement: HTMLElement | null,
  cb: (state: ScrollStateType) => void
): ScrollStateType => {
  let rAF: number | null = null;
  const element = viewAreaElement as HTMLElement;
  const state = {
    right: true,
    down: true,
    lastX: element.scrollLeft,
    lastY: element.scrollTop,
    subscriber: {},
  };

  const debounceScroll = (): void => {
    if (rAF) {
      return;
    }
    // schedule an invocation of scroll for next animation frame.
    rAF = window.requestAnimationFrame(() => {
      rAF = null;

      const currentX = element.scrollLeft;
      const { lastX } = state;
      if (currentX !== lastX) {
        state.right = currentX > lastX;
      }
      state.lastX = currentX;
      const currentY = element.scrollTop;
      const { lastY } = state;
      if (currentY !== lastY) {
        state.down = currentY > lastY;
      }
      state.lastY = currentY;
      cb(state);
    });
  };

  const subscriber = fromEvent(element, 'scroll')
    .pipe(throttleTime(150), debounceTime(150))
    .subscribe(debounceScroll);

  state.subscriber = subscriber;

  return state;
};

export const scrollIntoView = (
  element: HTMLElement,
  spot?: { top: number },
  skipOverflowHiddenElements = false
): void => {
  let parent = element.offsetParent as HTMLElement;
  let offsetY = element.offsetTop + element.clientTop;

  if (!parent) {
    return; // no need to scroll
  }
  while (
    (parent.clientHeight === parent.scrollHeight &&
      parent.clientWidth === parent.scrollWidth) ||
    (skipOverflowHiddenElements &&
      getComputedStyle(parent).overflow === 'hidden')
  ) {
    if (parent.dataset._scaleY) {
      offsetY /= parseInt(parent.dataset._scaleY, 10);
    }
    offsetY += parent.offsetTop;
    parent = parent.offsetParent as HTMLElement;
    if (!parent) {
      return; // no need to scroll
    }
  }

  if (spot) {
    if (spot.top !== undefined) {
      offsetY += spot.top;
    }
  }

  parent.scrollTop = offsetY;
};

export const scaleCheck = (scale: number): number => {
  if (typeof scale === 'number' && scale >= 50 && scale <= 250) {
    return Math.round(scale * 100) / 10000;
  }
  if (scale < 50) {
    return 0.5;
  }
  return 2.5;
};

export const downloadFileWithUri = (name: string, uri: string): void => {
  const ele = document.createElement('a');
  ele.download = name;
  ele.href = uri;
  ele.target = '_blank';
  document.body.appendChild(ele);
  ele.click();
  document.body.removeChild(ele);
  ele.remove();
};

export const uploadFile = (extension: string): Promise<any> =>
  new Promise(resolve => {
    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = extension;
    fileInput.onchange = (): void => {
      if (fileInput.files) {
        const reader = new FileReader();
        reader.onload = (): void => {
          const contents = reader.result;
          resolve(contents);
        };

        if (extension.includes('xfdf')) {
          reader.readAsText(fileInput.files[0]);
        } else {
          reader.readAsDataURL(fileInput.files[0]);
        }
      }
    };
    document.body.appendChild(fileInput);
    fileInput.click();
  });

const componentToHex = (c: number): string => {
  const hex = c.toString(16);
  return hex.length === 1 ? `0${hex}` : hex;
};

export const hexToRgb = (hex: string): any => {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result
    ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16),
      }
    : null;
};

export const rgbToHex = (r: number, g: number, b: number): string =>
  `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;

export const floatToHex = (r: number, g: number, b: number): string =>
  rgbToHex(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255));

type ScaleDataType = CoordType & {
  operator: string;
  clickX: number;
  clickY: number;
  moveX: number;
  moveY: number;
};

export const calcDragAndDropScale = ({
  top,
  left,
  width = 0,
  height = 0,
  operator,
  clickX,
  clickY,
  moveX,
  moveY,
}: ScaleDataType): CoordType => {
  let scaleCoord = {
    left,
    top,
    width,
    height,
  };

  switch (operator) {
    case 'top':
      scaleCoord = {
        ...scaleCoord,
        top: moveY - (clickY - top),
        height: height + (clickY - moveY),
      };
      break;
    case 'top-left':
      scaleCoord = {
        top: moveY - (clickY - top),
        left: moveX - (clickX - left),
        height: height + (clickY - moveY),
        width: width + (clickX - moveX),
      };
      break;
    case 'left':
      scaleCoord = {
        ...scaleCoord,
        left: moveX - (clickX - left),
        width: width + (clickX - moveX),
      };
      break;
    case 'bottom-left':
      scaleCoord = {
        ...scaleCoord,
        left: moveX - (clickX - left),
        width: width + (clickX - moveX),
        height: height + moveY - clickY,
      };
      break;
    case 'bottom':
      scaleCoord = {
        ...scaleCoord,
        height: height + moveY - clickY,
      };
      break;
    case 'bottom-right':
      scaleCoord = {
        ...scaleCoord,
        width: width + moveX - clickX,
        height: height + moveY - clickY,
      };
      break;
    case 'right':
      scaleCoord = {
        ...scaleCoord,
        width: width + moveX - clickX,
      };
      break;
    case 'top-right':
      scaleCoord = {
        ...scaleCoord,
        top: moveY - (clickY - top),
        width: width + moveX - clickX,
        height: height + (clickY - moveY),
      };
      break;
    default:
      break;
  }

  return scaleCoord;
};

const FRACTIONDIGITS = 2;

type NormalizeRoundFunc = (num: number, fractionDigits?: number) => number;

export const normalizeRound: NormalizeRoundFunc = (num, fractionDigits) => {
  const frac = fractionDigits || FRACTIONDIGITS;
  return Math.round(num * 10 ** frac) / 10 ** frac;
};

const printDocument = (documentId: string): void => {
  const doc = document.getElementById(documentId) as HTMLElement;
  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
  // @ts-ignore
  if (typeof doc.print === 'undefined') {
    setTimeout(() => {
      printDocument(documentId);
    }, 1000);
  } else {
    setTimeout(() => {
      // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
      // @ts-ignore
      doc.print();
    }, 3000);
  }
};

export const printPdf = async (url: string) => {
  // eslint-disable-next-line global-require
  const printJS = require('print-js');
  const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob);

  await fetch(url, {
    headers: {
      'Content-type': 'application/pdf',
    },
  })
    .then(data => data.blob())
    .then(res => {
      if (isIE11) {
        const wrapper = document.getElementById('embed-wrapper') as HTMLElement;
        wrapper.style.display = 'flex';
        const ele = document.createElement('embed');
        ele.setAttribute('type', 'application/pdf');
        ele.setAttribute('id', 'pdf-embed');
        ele.style.width = '70%';
        ele.style.height = '70%';
        ele.style.position = 'fixed';
        ele.style.top = '0';
        ele.style.left = '0';
        ele.style.bottom = '0';
        ele.style.right = '0';
        ele.style.margin = 'auto';
        ele.setAttribute('src', url);
        wrapper.appendChild(ele);

        printDocument('pdf-embed');
      } else {
        const fileReader = new FileReader();
        fileReader.readAsDataURL(res);
        fileReader.onload = fileLoadedEvent => {
          if (fileLoadedEvent.target) {
            const base64 = fileLoadedEvent.target.result as string;

            printJS({
              printable: base64.replace('data:application/pdf;base64,', ''),
              type: 'pdf',
              base64: true,
              showModal: true,
            });
          }
        };
      }
    });
};

export const strip = (number: number): number => {
  return parseFloat(parseFloat(number.toString()).toPrecision(12));
};
