import { makeAutoObservable } from "mobx";
import eventBus from "../../events";
import { FontLoadedNotify } from "../../events/eventName";
import utils from "../../utils/utils";
import {TrackType} from "../../enum/track_type";
import { checkFont, loadFont } from "../../utils/fontUtils";
import { maxDistance } from '../../utils/offset';
import {
  player,
} from "../../pages/MixedTrack/logic/initStructure";

const isFlag = (material, p) => {
  return material.flag & (1 << p)
}

// 文字的可视化对象
class TextGraph {

  constructor({
    material,
    zoom = 1.0,
    offsetX = 0,
    offsetY = 0,
    max_width = 0.0,
  }) {
    this.material = material
    this.zoom = zoom               // 缩放比 (使用zoom区别一下scale变量)
    this.max_width = max_width
    this.calcSize()                // 计算宽高
    this.offset = {                // 偏移: 画布中心点百分比, 例: 0.5 = 50%
      x: offsetX,
      y: offsetY,
    }
    makeAutoObservable(this);

    eventBus.on(FontLoadedNotify, this.fontLoaded)
  }

  fontLoaded = (val) => {
    if (val.name === this.material.font_name) {
      this.calcSize();
      eventBus.removeListener(FontLoadedNotify, this.fontLoaded)
    }
  }

  static padLineSpace = 2; // 行间距补偿(参照剪映)

  setData = (data) => {
    if (!data){
      return
    }
    if (data.zoom||data.zoom==0){
      this.zoom = data.zoom;
    }
    if (data?.offset) this.offset = data.offset;
    this.max_width = data.max_width;
    this.calcSize();
  }

  // 计算出宽高
  // 这个是基于canvas的尺寸，canvas画布默认高度为600
  // TODO
  // 1. 支持旋转
  // 2. 特殊字体或设置阴影时，计算有误差
  // 3. [OK]支持多行文本
  // 4. [OK]支持字间距、行间距
  // 5. 口播字幕固定宽度，超出自动换行
  calcSize(lockWidth=true) {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext('2d');

    if (!this.max_width) {
      const csize = player.size.getCanvasSize()
      this.max_width = csize.width * 0.8
    }

    // console.log("calcSize: ", this.max_width)

    // 设置文字属性与样式
    TextGraph.setTextProps(ctx, this.material);

    // 初始返回值
    let width = 0, height = 0;

    const padding = this.material.font_size / 3;
    const letterSpaceFactor = 1.4;
    const lineSpaceFactor = 1.4;
    const underLineSpace = this.material.underline ? this.material.content.split("\n").length * 0.8 : 0;

    if (this.material.alignment < 3) { // 排列水平方向: 0左 1中 2右
      // let chars = this.material.content.split("");
      //TODO
      let chars =[];
      let lineWidth = 0;
      let lineHeight = this.material.font_size * 2;
      let i = 0;
      while (i < chars.length) {
        const char = chars[i];
        if (char === "\n") {
          lineWidth -= this.material.letterSpacing * letterSpaceFactor;
          if (width < lineWidth) {
            width = lineWidth;
          }
          lineWidth = 0
          height += lineHeight + (this.material.lineSpacing + TextGraph.padLineSpace) * lineSpaceFactor;
          i++;
          continue
        }
        const metrics = ctx.measureText(char);
        if (lockWidth && this.material.type === TrackType.CaptionsTrack && ((lineWidth + metrics.width) * this.zoom > this.max_width)) {
          chars.splice(i, 0, "\n");
          width = lineWidth;
          lineWidth = 0;
          height += lineHeight + (this.material.lineSpacing + TextGraph.padLineSpace) * lineSpaceFactor;
          i++;
          continue
        }
        lineWidth += metrics.width + this.material.letterSpacing * letterSpaceFactor;
        i++;
      }

      // 修正数据
      lineWidth -= this.material.letterSpacing * letterSpaceFactor;
      if (width < lineWidth) {
        width = lineWidth;
      }
      height += lineHeight;
      this.material.rewriteContent = chars.join("")
    } else { // 垂直方向
      let texts = this.material.content.split("\n");
      // 计算
      for (const lineText of texts) {
        let chars = lineText.split("");
        // 记录单行文本宽高
        let lineWidth = 0, lineHeight = 0;
        // 逐字计算
        for (const char of chars) {
          const code = char.charCodeAt(0)
          const isLetter = code <= 122 && code >= 65;
          // const isNumber = code >= 48 && code <= 57;
          const metrics = ctx.measureText(char);
          let charHeight = Math.max(this.material.font_size * 2, (-metrics.actualBoundingBoxAscent) + metrics.actualBoundingBoxDescent);
          let charWidth = metrics.width;

          if (isLetter) {
            [charHeight, charWidth] = [charWidth, this.material.font_size * 2];
          }

          lineHeight += charHeight + this.material.letterSpacing * letterSpaceFactor;
          if (lineWidth < charWidth) {
            lineWidth = charWidth;
          }
        }
        lineHeight -= this.material.letterSpacing * letterSpaceFactor;
        // 高度选最大的
        if (lineHeight > height) {
          height = lineHeight;
        }
        // 累加行单文本宽度
        width += lineWidth;
      }
      // 累加行间距
      width += (texts.length - 1) * (this.material.lineSpacing + TextGraph.padLineSpace) * lineSpaceFactor;
    }

    let underLineWSpace = 0, underLineHSpace = 0;
    if (this.material.alignment >= 3) {
      underLineWSpace = underLineSpace;
    } else {
      underLineHSpace = underLineSpace;
    }

    this.width = (width + padding * 2 + underLineWSpace) * this.zoom;
    this.height = (height + padding * 2 + underLineHSpace) * this.zoom;

    // console.log(`calcSize: `, { text: this.material.rewriteContent || this.material.content, w: this.width, h: this.height, zoom: this.zoom });

    canvas.remove();
  }

  // 花字计算大小
  static calSizeWithSpe({ material, text = "花字", zoom = 1.0 }) {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext('2d');

    // console.log("calSizeWithSpe: ", this.material);
    // 设置文字属性与样式
    TextGraph.setTextProps(ctx, material);

    const metrics = ctx.measureText(text);
    const height = (-metrics.actualBoundingBoxAscent) + metrics.actualBoundingBoxDescent;

    canvas.remove();

    return { w: metrics.width * zoom, h: height * zoom };
  }

  static async setFont(ctx, material, factor = 2.0) {
    // 字体样式
    let fonts = [];
    if (material.italic) {
      fonts.push("oblique");
    }
    if (material.bold) {
      fonts.push("900");
    } else {
      fonts.push("400");
    }
    fonts.push(`${material.font_size * factor}px`);
    if (material.font_name) {
      // 查看字体是否存在，不存在则加载字体
      if (!checkFont(material.font_name)) {
        loadFont({ url: material.font_path, name: material.font_name });
      }
      fonts.push(material.font_name);
    }
    ctx.font = fonts.join(" ");
  }

  static setTextProps(ctx, material) {
    // 设置字体
    TextGraph.setFont(ctx, material, 2);

    // 文字颜色
    ctx.fillStyle = material.text_color;
    // 画笔颜色
    ctx.strokeStyle = material.text_color;

    // 保证文字位置，固定为top
    ctx.textBaseline = "top";

    // 对齐方式，固定为center
    // ctx.textAlign = "center"

    // 文本方向。可能的值包括：ltr, rtl, inherit。默认值是 inherit。
    // ctx.direction = 
    // 阴影
    if (isFlag(material, 5) && material.shadow_color) { 
      ctx.shadowColor = utils.hexToRgba(material.shadow_color, material.shadow_color_alpha);
      if (material.shadow_offset_x) {
        ctx.shadowOffsetX = material.shadow_offset_x;
      }
      if (material.shadow_offset_y) {
        ctx.shadowOffsetY = material.shadow_offset_y;
      }
      if (material.shadow_blur) {
        ctx.shadowBlur = material.shadow_blur;
      }
    }

    // 描边
    if (isFlag(material, 3) && material.border_color) {
      ctx.strokeStyle = material.border_color;
      if (material.border_width) {
        // 0.2*font_size*border_width/100
        // ctx.lineWidth = 0.2 * material.font_size * 2 * material.border_width / 100;
        ctx.lineWidth = material.font_size * material.border_width / 100 * 0.7;
        ctx.lineJoin = "round";
      }
    }
  }

  static calTextInfo(ctx, material, offset, size, zoom) {
    // 记录文本绘制参数
    let textInfo = [];

    const padding = material.font_size / 3;
    const letterSpaceFactor = 1.4;
    const lineSpaceFactor = 1.4;
    const underLineSpace = (material.underline ? 0.8: 0);

    // 分行
    let texts = (material.rewriteContent || material.content).split("\n");
    // console.log(`calcSize: `, texts);

    // 左对齐
    if (material.alignment == 0) {
      let { x, y } = offset;
      x += padding;
      y += padding;
      console.log("左对齐计算后的缩进x: ", x)
      for (const lineText of texts) {
        let lineHeight = 0;
        // const lineWidth = ctx.measureText(lineText).width + material.letterSpacing * letterSpaceFactor * (lineText.split("").length - 1);
        for (let index = 0; index < lineText.length; index++) {
          const char = lineText[index];
          const metrics = ctx.measureText(char);
          const charHeight = Math.max(material.font_size * 2, (-metrics.actualBoundingBoxAscent) + metrics.actualBoundingBoxDescent);
          const charWidth = metrics.width;
          // const charWidth = material.font_size, charHeight = material.font_size;
          textInfo.push({ text: char, x, y });
          if (index != lineText.length - 1) {
            x += material.letterSpacing * letterSpaceFactor;
          }
          x += charWidth
          if (lineHeight < charHeight) {
            lineHeight = charHeight;
          }
        }
        y += lineHeight + (material.lineSpacing + TextGraph.padLineSpace) * lineSpaceFactor + underLineSpace;
        // 下一行复位x
        x = offset.x + padding;
      }
    }

    // 居中对齐
    if (material.alignment == 1) {
      let { x, y } = offset;
      y += padding;
      for (const lineText of texts) {
        // console.log("calcSize lineText", lineText)
        const metrics = ctx.measureText(lineText);
        // const lineHeight = Math.max(material.font_size * 2, (-metrics.actualBoundingBoxAscent) + metrics.actualBoundingBoxDescent);
        const lineHeight = material.font_size * 2;
        const lineWidth = metrics.width + material.letterSpacing * letterSpaceFactor * (lineText.split("").length - 1);
        // 计算x的缩进
        x = offset.x + (size.w - lineWidth) / 2;

        console.log("居中对齐计算后的缩进x: ", x, size.w, metrics.width, (size.w - lineWidth) / 2)

        // 继续
        for (const char of lineText) {
          textInfo.push({ text: char, x, y });
          // 绘过以后计算下一个文本偏移
          const { width } = ctx.measureText(char);
          x += width + material.letterSpacing * letterSpaceFactor;
        }

        // 换行时offset.y累加
        y += lineHeight + (material.lineSpacing + TextGraph.padLineSpace) * lineSpaceFactor + underLineSpace;
      }
    }

    // 右对齐
    if (material.alignment == 2) {
      let { x, y } = offset;
      y += padding;
      for (const lineText of texts) {
        const metrics = ctx.measureText(lineText);
        const lineHeight = Math.max(material.font_size * 2, (-metrics.actualBoundingBoxAscent) + metrics.actualBoundingBoxDescent);
        const lineWidth = metrics.width + material.letterSpacing * letterSpaceFactor * (lineText.split("").length - 1);
        x = offset.x + (size.w - lineWidth - padding);

        // 继续
        for (const char of lineText) {
          textInfo.push({ text: char, x, y });
          // 绘过以后计算下一个文本偏移
          const { width } = ctx.measureText(char);
          x += width + material.letterSpacing * letterSpaceFactor;
        }

        // 换行时offset.y累加
        y += lineHeight + (material.lineSpacing + TextGraph.padLineSpace) * lineSpaceFactor + underLineSpace;
      }
    }

    // 垂直对齐
    if (material.alignment >= 3) {
      let { x, y } = offset;

      x = offset.x + padding + underLineSpace;
      for (const lineText of texts.reverse()) {

        y = offset.y + padding;
        let maxLineWidth = 0;

        const tempInfo = [];
        const lineCharHeight = []; // 保存行内文字高度
        for (const char of lineText) {
          const code = char.charCodeAt(0)
          const hvLetter = code <= 122 && code >= 65;

          tempInfo.push({ text: char, x, y, hvLetter });

          // 计算下一个文本偏移
          const metrics = ctx.measureText(char);
          let charHeight = Math.max(material.font_size * 2, (-metrics.actualBoundingBoxAscent) + metrics.actualBoundingBoxDescent);
          let charWidth = metrics.width;

          // 判断是否英文字符
          if (hvLetter) {
            [charHeight, charWidth] = [charWidth, material.font_size * 2];
          }

          if (maxLineWidth < charWidth) {
            maxLineWidth = charWidth;
          }

          lineCharHeight.push(charHeight);

          y += charHeight + material.letterSpacing * letterSpaceFactor;
        }

        // 修正本行初始y值
        const lineHeight = lineCharHeight.reduce((a, b) => a + b, 0) + (lineCharHeight.length - 1) * material.letterSpacing * letterSpaceFactor;
        let fixed = 0;
        if (material.alignment === 4) {
           fixed = (size.h - lineHeight) / 2 - padding;
        } else if (material.alignment === 5) {
           fixed = (size.h - lineHeight) - padding * 2;
        }
        textInfo.push(...tempInfo.map((i)=> Object.assign(i, {y: i.y + fixed}) ));

        x += maxLineWidth + (material.lineSpacing + TextGraph.padLineSpace) * lineSpaceFactor + underLineSpace;
      }
    }

    return textInfo;
  }

  getSize = (scale) => {
    return {
      width: this.width * scale,
      height: this.height * scale,
    };
  }

  getOffsetPercent = () => {
    return {
      x: this.offset.x + 0.5,
      y: this.offset.y + 0.5,
    }
  }

  getZoomSize = () => {
    return {
      w: this.width * (1 / this.zoom),
      h: this.height * (1 / this.zoom),
    }
  }

  getStart = (size, inCanvas = false) => {
    // 相对盒模型
    const destSize = inCanvas ? size.getCanvasSize() : size.getSize();
    const scale = destSize.height / size.canvasHeight;
    const box = this.getSize(scale);
    // console.log("getStart: ", destSize, scale, box)
    // 取到字幕中心点坐标
    const center = {
      x: (this.offset.x + 0.5) * destSize.width,
      y: (this.offset.y + 0.5) * destSize.height,
    }
    // 返回左上角坐标
    return {
      x: center.x - box.width / 2,
      y: center.y - box.height / 2,
    }
  }

  setOffset = (offsetX, offsetY) => {
    this.offset.x = offsetX;
    this.offset.y = offsetY;
  }

  setZoom = (zoom) => {
    if (zoom<0.5){
      zoom=0.5;
    }
    this.max_width = this.max_width / this.zoom * zoom;
    this.zoom = zoom;
    this.calcSize();
  }

  getScale = (size) => {
    return size.height / size.canvasHeight;
  }

  isPointInPath = (x, y, size) => {
    const scale = size.height / size.canvasHeight;
    // 获得
    const { width, height } = this.getSize(scale);

    var canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    // canvas.style.zIndex = 999;
    var ctx = canvas.getContext('2d');
    // console.log("scale: ", offset, this.getScaledWidth(scale), this.getScaledHeight(scale));
    // 开始画路径
    ctx.beginPath();
    ctx.rect(0, 0, width, height);
    ctx.closePath();
    // ctx.fillStyle = "red";
    // ctx.fillRect(offset.x, offset.y, this.getScaledWidth(scale), this.getScaledHeight(scale));

    // 原点补充
    const offset = this.getStart(size, false);
    x -= offset.x;
    y -= offset.y;

    var result = ctx.isPointInPath(x, y);
    canvas.remove();
    return result;
  }

  // 绘制花字效果
  static drawTextEffect = ({ ctx, material, text = "花字", width = 80, height = 80 }) => {
    // 计算offset位置
    const size = TextGraph.calSizeWithSpe({ material: material })
    const offset = { x: (width - size.w) / 2, y: (height - size.h) / 2 };

    // console.log("drawTextEffect: ", material);

    // 设置字体样式
    TextGraph.setTextProps(ctx, material);

    // 描边
    if (isFlag(material, 3) && material.border_color) {
      ctx.strokeText(text, offset.x, offset.y);
    }

    // 绘制文本
    ctx.fillText(text, offset.x, offset.y);
  }

  draw = (ctx, size) => {
    ctx.save();
    // 缩放
    if (this.zoom != 1.0) ctx.scale(this.zoom, this.zoom);

    // 计算文本偏移
    let offset = this.getStart(size, true);
    offset.x *= (1 / this.zoom);
    offset.y *= (1 / this.zoom);

    // 计算文本大小
    const textSize = this.getZoomSize();

    // 透明度
    ctx.globalAlpha = this.material.text_alpha;

    // 绘制背景
    if (this.material.isFlag(4) && this.material.background_color) {
      ctx.fillStyle = utils.hexToRgba(this.material.background_color, this.material.background_alpha);
      ctx.fillRect(offset.x, offset.y, textSize.w, textSize.h);
    }

    // 设置字体样式
    TextGraph.setTextProps(ctx, this.material);

    // 计算文本排列后的位置
    var textInfo = TextGraph.calTextInfo(ctx, this.material, offset, textSize, this.zoom);

    // 实际绘制文本
    for (const info of textInfo) {
      ctx.save();
      if (info.hvLetter) {
        ctx.textBaseline = "bottom";
        ctx.translate(info.x, info.y);
        ctx.rotate(90 * Math.PI / 180);
        ctx.translate(-info.x, -info.y);
      }
      if (isFlag(this.material, 3) && this.material.border_color) {
        ctx.strokeText(info.text, info.x, info.y);
      }
      ctx.fillText(info.text, info.x, info.y);
      ctx.restore();
    }

    // 下划线
    if (this.material.underline) {
      if (this.material.border_color) {
        this.paintUnderline(ctx, textInfo, 3);
        ctx.strokeStyle = this.material.text_color;
      }
      this.paintUnderline(ctx, textInfo, 1);
    }

    // 恢复画布状态
    ctx.restore();
  }

  paintUnderline(ctx, textInfo, lineWidth = 3) {
      console.log("paintUnderline: ", lineWidth * this.zoom);
      ctx.lineCap = "round";
      ctx.lineWidth = lineWidth * this.zoom;
      ctx.beginPath();
      // 通过此值判断是否新行
      let x = 0, y = 0;
      for (const info of textInfo) {
        const metrics = ctx.measureText(info.text);
        const charWidth = metrics.width;
        // 使用实际高度，文本不一致情况下高度有误差
        // const charHeight = (-metrics.actualBoundingBoxAscent) + metrics.actualBoundingBoxDescent;
        const charHeight = this.material.font_size * 2;
        if (this.material.alignment < 3) { // 水平排列
          const newY = info.y + charHeight + 2 * this.zoom;
          const newX = info.x;
          if (y == 0 || newY != y) {
            y = newY;
            ctx.moveTo(newX, newY); // 新行则重新选择起点
          }
          ctx.lineTo(newX + charWidth, newY);
        } else {
          // 垂直排列
          const newX = info.x - 2 * this.zoom;
          const newY = info.y;
          if (x == 0 || newX != x) {
            x = newX;
            ctx.moveTo(newX, newY); // 新行则重新选择起点
          }
          ctx.lineTo(newX, newY + charHeight);
        }
      }
      ctx.stroke()
  }

  // 调整可视化大小
  onAngleResize = (offset, angle, size) => {
    const scale = size.height / size.canvasHeight;
    console.log("onAngleResize : scale = ", scale);
    let d = 0;
    // 计算宽高比
    const whScale = this.width / this.height || 0; 
    // 四角拖放时各参数不一样
    switch (angle) {
      case 0:
        d = -maxDistance(offset.x, offset.y)
        break
      case 1:
        d = maxDistance(offset.x, -offset.y)
        break
      case 2:
        d = maxDistance(offset.x, offset.y)
        break
      default:
        d = maxDistance(-offset.x, offset.y)
        break
    }

    const oldHeight = this.height;

    if (this.height + d / scale < 0) {
      d = -this.height * scale
    }

    // 宽高都变化
    let nWidth = this.width + d  / scale * whScale;
    let nHeight = this.height + d / scale;

    console.log("累加后的 w, h = ", nWidth, nHeight);

    // 防止口播超出指定宽度(画布宽度的80%)
    // if (!lock) {
    //   const csize = size.getCanvasSize()
    //   if (this.material.type === TrackType.CaptionsTrack && nWidth > csize.width * 0.8) {
    //     return false
    //   }
    // }

    // 重新计算zoom值
    const nZoom = this.zoom * (nHeight === 0 ? 0 : (nHeight / oldHeight));

    if (nZoom > 5.0 || nZoom < 0.5)  {
      return false
    }

    this.max_width = this.max_width / this.zoom * nZoom

    this.zoom = nZoom
    this.height = nHeight

    return true
  }

  calcMoveOffset = (x, y, size) => {
    // x, y 为屏幕坐标
    const scale = size.height / size.canvasHeight;
    const destSize = size.getCanvasSize();
    // console.log("onMove: ", size, scale, destSize)
    // 需要转换为canvas坐标，再换算成百分比
    return {
      x: this.offset.x + x / scale / destSize.width,
      y: this.offset.y + y / scale / destSize.height,
    }
  }

  // offset坐标系
  onMove = (x, y, size) => {
    const offset = this.calcMoveOffset(x, y, size)
    this.offset.x = offset.x
    this.offset.y = offset.y
  }

  toJson = () => {
    // 需要得到 当前canvas 的宽高
    const offset = {
      x: this.offset.x,
      y: this.offset.y,
    };
    return {
      offset: offset,
      transform: offset,
      zoom: this.zoom,
      max_width: this.max_width,
    }
  }
}

export default TextGraph