import { ModuleControl } from "queenjs";
import { reactive } from "vue";
import { EditorModule } from "../../module";
import { DesignComp } from "../../objects/DesignTemp/DesignComp";
import { Matrix } from "./Matrix";
import * as transforms from "./transforms";
import { GroupActionCtrl } from "./GroupCtrl";

type TransHandle = (this: TransferCtrl, e: MouseEvent) => void;

export type TransCreateFn = (...args: any[]) => {
  mousedown?: TransHandle;
  mousemove: TransHandle;
  mouseup: TransHandle;
};

export class TransferCtrl extends ModuleControl<EditorModule> {
  compEl!: HTMLElement;
  pageEl!: HTMLElement;
  currComp!: DesignComp;
  currTransfer!: ReturnType<TransCreateFn>;
  currObserver?: MutationObserver;
  transforms = transforms;

  groupCtrl = new GroupActionCtrl(this.module);

  transEvent = {
    startX: 0,
    startY: 0,
    offsetX: 0,
    offsetY: 0,
    width: 0,
    height: 0,
  };
  transferStyle = reactive({
    top: "",
    left: "",
    width: "",
    height: "",
    transform: {
      scale: 1,
      rotate: "0deg",
      translateX: "-50%",
      translateY: "-50%",
    },
  });

  originPiont = {
    x: 0,
    y: 0,
  };

  init(pageEl: HTMLElement) {
    this.currComp = this.module.store.currComp;
    this.compEl = this.currComp.$el;
    this.pageEl = pageEl;
    this.observe();
  }

  mousedown(e: MouseEvent, type: keyof typeof transforms, currComp?: any) {
    if (!currComp) {
      currComp = this.store.currComp;
    }
    this.currComp = currComp;
    this.compEl = currComp.$el;
    this.transEvent = {
      startX: e.clientX,
      startY: e.clientY,
      offsetX: 0,
      offsetY: 0,
      width: currComp.layout.size?.[0] || currComp.$el.clientWidth * 2,
      height: currComp.layout.size?.[1] || currComp.$el.clientHeight * 2,
    };
    this.currTransfer = this.transforms[type]();
    document.addEventListener("mousemove", this.mousemove);
    document.addEventListener("mouseup", this.mouseup);
    this.currTransfer.mousedown?.call(this, e);
  }

  private mousemove = (e: MouseEvent) => {
    const { transEvent } = this;
    transEvent.offsetX = e.clientX - transEvent.startX;
    transEvent.offsetY = e.clientY - transEvent.startY;
    this.currTransfer.mousemove.call(this, e);
  };
  private mouseup = (e: MouseEvent) => {
    document.removeEventListener("mousemove", this.mousemove);
    document.removeEventListener("mouseup", this.mouseup);
    this.currTransfer.mouseup.call(this, e);
  };

  observe() {
    if (this.currObserver) {
      this.currObserver.disconnect();
    }
    this.initStyle();
    this.currObserver = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (
          mutation.type === "childList" ||
          (mutation.type === "attributes" && mutation.attributeName === "style")
        ) {
          this.initStyle();
        }
      });
    });
    this.currObserver.observe(this.compEl, {
      attributes: true,
      childList: true,
      subtree: true,
      characterData: true,
    });
  }

  initStyle() {
    if (!this.compEl) return;

    const rect = this.compEl.getBoundingClientRect();
    const pageRect = this.pageEl.getBoundingClientRect();

    const matrix = new Matrix();
    matrix.setFormDiv(this.compEl);

    const width = this.compEl.clientWidth * matrix.getScale();
    const height = this.compEl.clientHeight * matrix.getScale();

    this.transferStyle.width = width + "px";
    this.transferStyle.height = height + "px";
    this.transferStyle.top = rect.top + rect.height / 2 - pageRect.top + "px";
    this.transferStyle.left = rect.left + rect.width / 2 - pageRect.left + "px";
    if (!this.transferStyle.transform) {
      this.transferStyle.transform = {
        scale: 1,
        rotate: "0deg",
        translateX: "-50%",
        translateY: "-50%",
      };
    }
    this.transferStyle.transform.scale = matrix.getScale();
    this.transferStyle.transform.rotate = matrix.getRotate() + "deg";
    this.transferStyle.transform.translateY = "-" + height / 2 + "px";

    this.originPiont.x = rect.left + rect.width / 2;
    this.originPiont.y = rect.top + rect.height / 2;
  }

  resetStyle() {
    Object.keys(this.transferStyle).forEach((key) => {
      (this.transferStyle as any)[key] = "";
    });
  }

  destroy() {
    this.groupCtrl.destroy();
    this.currObserver?.disconnect();
    this.resetStyle();
  }
}
