import CreateDom from "@/lib/main-entrance/CreateDom";
// 导入截图所需样式
import "@/assets/scss/screen-short.scss";
import InitData from "@/lib/main-entrance/InitData";
import {
  cutOutBoxBorder,
  drawCutOutBoxReturnType,
  movePositionType,
  positionInfoType,
  zoomCutOutBoxReturnType
} from "@/lib/type/ComponentType";
import { drawMasking } from "@/lib/split-methods/DrawMasking";
import { fixedData, nonNegativeData } from "@/lib/common-methords/FixedData";
import { drawPencil, initPencil } from "@/lib/split-methods/DrawPencil";
import { drawText } from "@/lib/split-methods/DrawText";
import { drawRectangle } from "@/lib/split-methods/DrawRectangle";
import { drawCircle } from "@/lib/split-methods/DrawCircle";
import { drawLineArrow } from "@/lib/split-methods/DrawLineArrow";
import { drawMosaic } from "@/lib/split-methods/DrawMosaic";
import { drawCutOutBox } from "@/lib/split-methods/DrawCutOutBox";
import { zoomCutOutBoxPosition } from "@/lib/common-methords/ZoomCutOutBoxPosition";
import { saveBorderArrInfo } from "@/lib/common-methords/SaveBorderArrInfo";
import { calculateToolLocation } from "@/lib/split-methods/CalculateToolLocation";
import html2canvas from "html2canvas";
import PlugInParameters from "@/lib/main-entrance/PlugInParameters";

export default class ScreenShort {
  // 当前实例的响应式data数据
  private readonly data: InitData;

  // video容器用于存放屏幕MediaStream流
  private readonly videoController: HTMLVideoElement;
  // 截图区域canvas容器
  private readonly screenShortController: HTMLCanvasElement | null;
  // 截图工具栏dom
  private readonly toolController: HTMLDivElement | null;
  // 截图图片存放容器
  private screenShortImageController: HTMLCanvasElement;
  // 截图区域画布
  private screenShortCanvas: CanvasRenderingContext2D | undefined;
  // 文本区域dom
  private readonly textInputController: HTMLDivElement | null;
  //  截图工具栏画笔选项dom
  private optionController: HTMLDivElement | null;
  private optionIcoController: HTMLDivElement | null;
  // 图形位置参数
  private drawGraphPosition: positionInfoType = {
    startX: 0,
    startY: 0,
    width: 0,
    height: 0
  };
  // 临时图形位置参数
  private tempGraphPosition: positionInfoType = {
    startX: 0,
    startY: 0,
    width: 0,
    height: 0
  };
  // 裁剪框边框节点坐标事件
  private cutOutBoxBorderArr: Array<cutOutBoxBorder> = [];
  // 当前操作的边框节点
  private borderOption: number | null = null;

  // 点击裁剪框时的鼠标坐标
  private movePosition: movePositionType = {
    moveStartX: 0,
    moveStartY: 0
  };

  // 鼠标点击状态
  private clickFlag = false;
  private fontSize = 17;
  // 最大可撤销次数
  private maxUndoNum = 15;
  // 马赛克涂抹区域大小
  private degreeOfBlur = 5;

  // 文本输入框位置
  private textInputPosition: { mouseX: number; mouseY: number } = {
    mouseX: 0,
    mouseY: 0
  };
  constructor(options: {
    enableWebRtc: boolean;
    level: number;
    completeCallback: Function;
  }) {
    const plugInParameters = new PlugInParameters();
    // webrtc启用状态
    if (
      options &&
      Object.prototype.hasOwnProperty.call(options, "enableWebRtc")
    ) {
      plugInParameters.setWebRtcStatus(options.enableWebRtc);
    }
    // 设置回调函数
    if (
      options &&
      Object.prototype.hasOwnProperty.call(options, "completeCallback")
    ) {
      // 创建dom
      new CreateDom(options.completeCallback);
    } else {
      // 创建dom
      new CreateDom((base64: string) => {
        sessionStorage.setItem("screenShotImg", base64);
      });
    }
    this.videoController = document.createElement("video");
    this.videoController.autoplay = true;
    this.screenShortImageController = document.createElement("canvas");
    // 实例化响应式data
    this.data = new InitData();
    // 获取截图区域canvas容器
    this.screenShortController = this.data.getScreenShortController() as HTMLCanvasElement | null;
    this.toolController = this.data.getToolController() as HTMLDivElement | null;
    this.textInputController = this.data.getTextInputController() as HTMLDivElement | null;
    this.optionController = this.data.getOptionController() as HTMLDivElement | null;
    this.optionIcoController = this.data.getOptionIcoController() as HTMLDivElement | null;
    this.load();
    const screenShotContainer = document.getElementById("screenShotContainer");
    if (screenShotContainer == null) return;
    // 调整层级
    screenShotContainer.style.zIndex = options?.level + "";
  }

  // 加载截图组件
  private load() {
    const plugInParameters = new PlugInParameters();
    // 设置截图区域canvas宽高
    this.data.setScreenShortInfo(window.innerWidth, window.innerHeight);
    // 设置截图图片存放容器宽高
    this.screenShortImageController.width = window.innerWidth;
    this.screenShortImageController.height = window.innerHeight;
    // 获取截图区域画canvas容器画布
    const context = this.screenShortController?.getContext("2d");
    if (context == null) return;
    // 启用webrtc截屏时则修改容器宽高
    if (plugInParameters.getWebRtcStatus()) {
      // 设置为屏幕宽高
      this.data.setScreenShortInfo(window.screen.width, window.screen.height);
      // 设置为屏幕宽高
      this.screenShortImageController.width = window.screen.width;
      this.screenShortImageController.height = window.screen.height;
    }
    // 显示截图区域容器
    this.data.showScreenShortPanel();
    if (!plugInParameters.getWebRtcStatus()) {
      // html2canvas截屏
      html2canvas(document.body, {}).then(canvas => {
        // 装载截图的dom为null则退出
        if (this.screenShortController == null) return;

        // 存放html2canvas截取的内容
        this.screenShortImageController = canvas;

        // 赋值截图区域canvas画布
        this.screenShortCanvas = context;
        // 绘制蒙层
        drawMasking(context);

        // 添加监听
        this.screenShortController?.addEventListener(
          "mousedown",
          this.mouseDownEvent
        );
        this.screenShortController?.addEventListener(
          "mousemove",
          this.mouseMoveEvent
        );
        this.screenShortController?.addEventListener(
          "mouseup",
          this.mouseUpEvent
        );
      });
      return;
    }
    // 截取整个屏幕
    this.screenShot();
  }

  // 开始捕捉屏幕
  private startCapture = async () => {
    let captureStream = null;

    try {
      // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
      // @ts-ignore
      // 捕获屏幕
      captureStream = await navigator.mediaDevices.getDisplayMedia();
      // 将MediaStream输出至video标签
      this.videoController.srcObject = captureStream;
    } catch (err) {
      // 销毁截图组件
      this.data.destroyDOM();
      throw `浏览器不支持webrtc或者用户未授权( ${err} )`;
    }
    return captureStream;
  };

  // 停止捕捉屏幕
  private stopCapture = () => {
    const srcObject = this.videoController.srcObject;
    if (srcObject && "getTracks" in srcObject) {
      const tracks = srcObject.getTracks();
      tracks.forEach(track => track.stop());
      this.videoController.srcObject = null;
    }
  };

  // 截屏
  private screenShot = () => {
    // 开始捕捉屏幕
    this.startCapture().then(() => {
      setTimeout(() => {
        // 获取截图区域canvas容器画布
        const context = this.screenShortController?.getContext("2d");
        if (context == null || this.screenShortController == null) return;

        // 赋值截图区域canvas画布
        this.screenShortCanvas = context;
        // 绘制蒙层
        drawMasking(context);
        // 将获取到的屏幕截图绘制到图片容器里
        this.screenShortImageController
          .getContext("2d")
          ?.drawImage(
            this.videoController,
            0,
            0,
            this.screenShortImageController?.width,
            this.screenShortImageController?.height
          );
        // 添加监听
        this.screenShortController?.addEventListener(
          "mousedown",
          this.mouseDownEvent
        );
        this.screenShortController?.addEventListener(
          "mousemove",
          this.mouseMoveEvent
        );
        this.screenShortController?.addEventListener(
          "mouseup",
          this.mouseUpEvent
        );
        // 停止捕捉屏幕
        this.stopCapture();
      }, 500);
    });
  };

  // 鼠标按下事件
  private mouseDownEvent = (event: MouseEvent) => {
    // 当前操作的是撤销
    if (this.data.getToolName() == "undo") return;
    this.data.setDragging(true);
    this.clickFlag = true;
    const mouseX = nonNegativeData(event.offsetX);
    const mouseY = nonNegativeData(event.offsetY);

    // 如果当前操作的是截图工具栏
    if (this.data.getToolClickStatus()) {
      // 记录当前鼠标开始坐标
      this.drawGraphPosition.startX = mouseX;
      this.drawGraphPosition.startY = mouseY;
    } else {
      // 隐藏截图工具栏
      this.data.setToolStatus(false);
    }

    // 当前操作的是画笔
    if (this.data.getToolName() == "brush" && this.screenShortCanvas) {
      // 初始化画笔
      initPencil(this.screenShortCanvas, mouseX, mouseY);
    }

    // 当前操作的文本
    if (
      this.data.getToolName() == "text" &&
      this.textInputController &&
      this.screenShortController &&
      this.screenShortCanvas
    ) {
      // 修改鼠标样式
      this.screenShortController.style.cursor = "text";
      // 显示文本输入区域
      this.data.setTextStatus(true);
      // 判断输入框位置是否变化
      if (
        this.textInputPosition.mouseX != 0 &&
        this.textInputPosition.mouseY != 0 &&
        this.textInputPosition.mouseX != mouseX &&
        this.textInputPosition.mouseY != mouseY
      ) {
        drawText(
          this.textInputController.innerText,
          this.textInputPosition.mouseX,
          this.textInputPosition.mouseY,
          this.data.getSelectedColor(),
          this.fontSize,
          this.screenShortCanvas
        );
        // 清空文本输入区域的内容
        this.textInputController.innerHTML = "";
        // 保存绘制记录
        this.addHistory();
      }
      // 计算文本框显示位置
      const textMouseX = mouseX - 15;
      const textMouseY = mouseY - 15;
      // 修改文本区域位置
      this.textInputController.style.left = textMouseX + "px";
      this.textInputController.style.top = textMouseY + "px";
      setTimeout(() => {
        // 获取焦点
        if (this.textInputController) {
          this.textInputController.focus();
          // 记录当前输入框位置
          this.textInputPosition = { mouseX: mouseX, mouseY: mouseY };
        }
      });
    }

    // 如果操作的是裁剪框
    if (this.borderOption) {
      // 设置为拖动状态
      this.data.setDraggingTrim(true);
      // 记录移动时的起始点坐标
      this.movePosition.moveStartX = mouseX;
      this.movePosition.moveStartY = mouseY;
    } else {
      // 绘制裁剪框,记录当前鼠标开始坐标
      this.drawGraphPosition.startX = mouseX;
      this.drawGraphPosition.startY = mouseY;
    }
  };

  // 鼠标移动事件
  private mouseMoveEvent = (event: MouseEvent) => {
    if (
      this.screenShortCanvas == null ||
      this.screenShortController == null ||
      this.data.getToolName() == "undo"
    )
      return;
    this.clickFlag = false;
    // 获取裁剪框位置信息
    const { startX, startY, width, height } = this.drawGraphPosition;
    // 获取当前鼠标坐标
    const currentX = nonNegativeData(event.offsetX);
    const currentY = nonNegativeData(event.offsetY);
    // 裁剪框临时宽高
    const tempWidth = currentX - startX;
    const tempHeight = currentY - startY;
    // 工具栏绘制
    if (this.data.getToolClickStatus() && this.data.getDragging()) {
      // 当前操作的不是马赛克则显示最后一次画布绘制时的状态
      if (this.data.getToolName() != "mosaicPen") {
        this.showLastHistory();
      }
      switch (this.data.getToolName()) {
        case "square":
          drawRectangle(
            startX,
            startY,
            tempWidth,
            tempHeight,
            this.data.getSelectedColor(),
            this.data.getPenSize(),
            this.screenShortCanvas,
            this.screenShortController,
            this.screenShortImageController
          );
          break;
        case "round":
          drawCircle(
            this.screenShortCanvas,
            currentX,
            currentY,
            startX,
            startY,
            this.data.getPenSize(),
            this.data.getSelectedColor()
          );
          break;
        case "right-top":
          drawLineArrow(
            this.screenShortCanvas,
            startX,
            startY,
            currentX,
            currentY,
            30,
            10,
            this.data.getPenSize(),
            this.data.getSelectedColor()
          );
          break;
        case "brush":
          // 画笔绘制
          drawPencil(
            this.screenShortCanvas,
            currentX,
            currentY,
            this.data.getPenSize(),
            this.data.getSelectedColor()
          );
          break;
        case "mosaicPen":
          // 绘制马赛克，为了确保鼠标位置在绘制区域中间，所以对x、y坐标进行-10处理
          drawMosaic(
            currentX - 10,
            currentY - 10,
            this.data.getPenSize(),
            this.degreeOfBlur,
            this.screenShortCanvas
          );
          break;
        default:
          break;
      }
      return;
    }
    // 执行裁剪框操作函数
    this.operatingCutOutBox(
      currentX,
      currentY,
      startX,
      startY,
      width,
      height,
      this.screenShortCanvas
    );
    // 如果鼠标未点击或者当前操作的是裁剪框都return
    if (!this.data.getDragging() || this.data.getDraggingTrim()) return;
    // 绘制裁剪框
    this.tempGraphPosition = drawCutOutBox(
      startX,
      startY,
      tempWidth,
      tempHeight,
      this.screenShortCanvas,
      this.data.getBorderSize(),
      this.screenShortController,
      this.screenShortImageController
    ) as drawCutOutBoxReturnType;
  };

  // 鼠标抬起事件
  private mouseUpEvent = () => {
    // 当前操作的是撤销
    if (this.data.getToolName() == "undo") return;
    // 绘制结束
    this.data.setDragging(false);
    this.data.setDraggingTrim(false);
    if (this.screenShortController == null || this.screenShortCanvas == null) {
      return;
    }
    if (this.data.getToolClickStatus()) {
      // 保存绘制记录
      this.addHistory();
      return;
    }
    // 保存绘制后的图形位置信息
    this.drawGraphPosition = this.tempGraphPosition;
    // 如果工具栏未点击则保存裁剪框位置
    if (!this.data.getToolClickStatus()) {
      const { startX, startY, width, height } = this.drawGraphPosition;
      this.data.setCutOutBoxPosition(startX, startY, width, height);
    }
    // 保存边框节点信息
    this.cutOutBoxBorderArr = saveBorderArrInfo(
      this.data.getBorderSize(),
      this.drawGraphPosition
    );
    if (this.screenShortController != null) {
      // 修改鼠标状态为拖动
      this.screenShortController.style.cursor = "move";
      // 显示截图工具栏
      this.data.setToolStatus(true);
      if (this.toolController != null) {
        // 计算截图工具栏位置
        const toolLocation = calculateToolLocation(
          this.drawGraphPosition,
          this.toolController.offsetWidth
        );
        // 显示并设置截图工具栏位置
        this.data.setToolInfo(toolLocation.mouseX, toolLocation.mouseY);
      }
    }
  };

  /**
   * 操作裁剪框
   * @param currentX 裁剪框当前x轴坐标
   * @param currentY 裁剪框当前y轴坐标
   * @param startX 鼠标x轴坐标
   * @param startY 鼠标y轴坐标
   * @param width 裁剪框宽度
   * @param height 裁剪框高度
   * @param context 需要进行绘制的canvas画布
   * @private
   */
  private operatingCutOutBox(
    currentX: number,
    currentY: number,
    startX: number,
    startY: number,
    width: number,
    height: number,
    context: CanvasRenderingContext2D
  ) {
    // canvas元素不存在
    if (this.screenShortController == null) {
      return;
    }
    // 获取鼠标按下时的坐标
    const { moveStartX, moveStartY } = this.movePosition;

    // 裁剪框边框节点事件存在且裁剪框未进行操作，则对鼠标样式进行修改
    if (this.cutOutBoxBorderArr.length > 0 && !this.data.getDraggingTrim()) {
      // 标识鼠标是否在裁剪框内
      let flag = false;
      // 判断鼠标位置
      context.beginPath();
      for (let i = 0; i < this.cutOutBoxBorderArr.length; i++) {
        context.rect(
          this.cutOutBoxBorderArr[i].x,
          this.cutOutBoxBorderArr[i].y,
          this.cutOutBoxBorderArr[i].width,
          this.cutOutBoxBorderArr[i].height
        );
        if (context.isPointInPath(currentX, currentY)) {
          switch (this.cutOutBoxBorderArr[i].index) {
            case 1:
              if (this.data.getToolClickStatus()) {
                this.screenShortController.style.cursor = "crosshair";
              } else {
                this.screenShortController.style.cursor = "move";
              }
              break;
            case 2:
              this.screenShortController.style.cursor = "ns-resize";
              break;
            case 3:
              this.screenShortController.style.cursor = "ew-resize";
              break;
            case 4:
              this.screenShortController.style.cursor = "nwse-resize";
              break;
            case 5:
              this.screenShortController.style.cursor = "nesw-resize";
              break;
            default:
              break;
          }
          this.borderOption = this.cutOutBoxBorderArr[i].option;
          flag = true;
          break;
        }
      }
      context.closePath();
      if (!flag) {
        // 鼠标移出裁剪框重置鼠标样式
        this.screenShortController.style.cursor = "default";
        // 重置当前操作的边框节点为null
        this.borderOption = null;
      }
    }

    // 裁剪框正在被操作
    if (this.data.getDraggingTrim()) {
      // 当前操作节点为1时则为移动裁剪框
      if (this.borderOption === 1) {
        // 计算要移动的x轴坐标
        const x = fixedData(
          currentX - (moveStartX - startX),
          width,
          this.screenShortController.width
        );
        // 计算要移动的y轴坐标
        const y = fixedData(
          currentY - (moveStartY - startY),
          height,
          this.screenShortController.height
        );
        // 重新绘制裁剪框
        this.tempGraphPosition = drawCutOutBox(
          x,
          y,
          width,
          height,
          context,
          this.data.getBorderSize(),
          this.screenShortController as HTMLCanvasElement,
          this.screenShortImageController
        ) as drawCutOutBoxReturnType;
      } else {
        // 裁剪框其他8个点的拖拽事件
        const {
          tempStartX,
          tempStartY,
          tempWidth,
          tempHeight
        } = zoomCutOutBoxPosition(
          currentX,
          currentY,
          startX,
          startY,
          width,
          height,
          this.borderOption as number
        ) as zoomCutOutBoxReturnType;
        // 绘制裁剪框
        this.tempGraphPosition = drawCutOutBox(
          tempStartX,
          tempStartY,
          tempWidth,
          tempHeight,
          context,
          this.data.getBorderSize(),
          this.screenShortController as HTMLCanvasElement,
          this.screenShortImageController
        ) as drawCutOutBoxReturnType;
      }
    }
  }

  /**
   * 显示最新的画布状态
   * @private
   */
  private showLastHistory() {
    if (this.screenShortCanvas != null) {
      const context = this.screenShortCanvas;
      if (this.data.getHistory().length <= 0) {
        this.addHistory();
      }
      context.putImageData(
        this.data.getHistory()[this.data.getHistory().length - 1]["data"],
        0,
        0
      );
    }
  }

  /**
   * 保存当前画布状态
   * @private
   */
  private addHistory() {
    if (this.screenShortCanvas != null && this.screenShortController != null) {
      // 获取canvas画布与容器
      const context = this.screenShortCanvas;
      const controller = this.screenShortController;
      if (this.data.getHistory().length > this.maxUndoNum) {
        // 删除最早的一条画布记录
        this.data.shiftHistory();
      }
      // 保存当前画布状态
      this.data.pushHistory({
        data: context.getImageData(0, 0, controller.width, controller.height)
      });
      // 启用撤销按钮
      this.data.setUndoStatus(true);
    }
  }
}
