import { DC, EntityShape } from "../../deconstruction";
import { Stage } from "konva/lib/Stage";
import {
  computed,
  getCurrentInstance,
  nextTick,
  onUnmounted,
  reactive,
  ref,
  shallowRef,
  watch,
  WatchCallback,
  watchEffect,
  WatchOptions,
  WatchSource,
} from "vue";
import { Mode } from "../../constant/mode.ts";
import { Layer } from "konva/lib/Layer";
import { Pos } from "@/utils/math.ts";
import { listener } from "@/utils/event.ts";
import { mergeFuns } from "@/utils/shared.ts";
import { DrawData } from "../components/index.ts";

export const installGlobalVar = <T>(
  create: () => { var: T; onDestroy: () => void } | T,
  key = Symbol("globalVar"),
  noRefDel = true
) => {
  let initialed = false;
  let refCount = 0;
  let onDestroy: (() => void) | null = null;

  const useGlobalVar = (): T => {
    const instance = getCurrentInstance() as any;
    const ctx = instance.appContext;
    if (!initialed) {
      let val = create() as any;
      if (typeof val === "object" && "var" in val && "onDestroy" in val) {
        onDestroy = val.onDestory;
        val = val.var;
      }
      ctx[key] = val;
      initialed = true;
    }
    return ctx[key];
  };

  return noRefDel
    ? () => {
        const instance = getCurrentInstance() as any;
        const ctx = instance.appContext;
        ++refCount;
        onUnmounted(() => {
          if (--refCount === 0 && noRefDel) {
            initialed = false;
            delete ctx[key];
            console.log("销毁", key);
            onDestroy && onDestroy();
            onDestroy = null;
          }
        });
        return useGlobalVar();
      }
    : useGlobalVar;
};

export type InstanceProps = {
  id?: string;
  data: DrawData;
  handlerResource(file: File): Promise<string>
}
export const useInstanceProps = installGlobalVar(() => {
  const props = ref<InstanceProps>()
  return {
    set(val: InstanceProps) {
      props.value = val
    },
    get() {
      return props.value!
    }
  }
}, Symbol('instanceId'))

export const stackVar = <T>(init?: T) => {
  const stack = reactive([init]) as T[];
  const result = {
    get value() {
      return stack[stack.length - 1];
    },
    set value(val) {
      stack[stack.length - 1] = val;
    },
    push(data: T) {
      stack.push(data);
    },
    pop() {
      if (stack.length - 1 > 0) {
        stack.pop();
      } else {
        console.error("已到达栈顶");
      }
    },
    cycle<R>(data: T, run: () => R): R {
      result.push(data);
      const r = run();
      result.pop();
      return r;
    },
  };
  return result;
};

export const globalWatch = <T>(
  source: WatchSource<T>,
  cb: WatchCallback<T, T>,
  options?: WatchOptions
): (() => void) => {
  let stop: () => void;
  nextTick(() => {
    stop = watch(source, cb as any, options as any);
  });
  return () => {
    stop && stop();
  };
};

export const useStage = installGlobalVar(
  () => shallowRef<DC<Stage> | undefined>(),               
  Symbol("stage")
);
export const useMode = installGlobalVar(() => {
  const stack = stackVar(new Set([Mode.write]))
  const modeStack = {
    ...stack,
    get value() {
      return stack.value
    },
    set value(val: Set<Mode>) {
      stack.value = val
    },
    push(...modes: Mode[]) {
      return stack.push(new Set(modes))
    },
    include(...modes: Mode[]) {
      return modes.every((m) => modeStack.value.has(m));
    },
    add(...modes: Mode[]) {
      modes.forEach((mode) => modeStack.value.add(mode));
    },
    del(...modes: Mode[]) {
      modes.forEach((mode) => modeStack.value.delete(mode));
    }
  }
  if (import.meta.env.DEV) {
    watchEffect(() => {
      console.error([...modeStack.value.values()].join(','))
    }, { flush: 'sync' })
  }
  return modeStack;
}, Symbol("mode"));
export const useCan = installGlobalVar(() => {
  const mode = useMode();
  const stage = useStage();
  const key = useDownKeys()
  const loaded = computed(() => !!stage.value?.getStage())

  // 鼠标是否可用
  const mouse = computed(() => loaded.value && !mode.include(Mode.static))

  // 可以进入拖拽模式
  const dragMode = computed(() => {
    if (!mouse.value || mode.include(Mode.readonly) || key.has(' ')) return false;
    return mode.include(Mode.draw) || mode.include(Mode.update)
  })

  // 是否在视图模式
  const viewMode = computed(() => {
    return mouse.value && (!mode.include(Mode.draging) || key.has(' '))
  })

  // shape是否可以对鼠标做出反应
  const mouseReact = computed(() => mouse.value && (mode.include(Mode.write) || mode.include(Mode.update)))

  // 可以进入编辑模式
  const editMode = computed(() => mouse.value && mode.include(Mode.write))

  // 可以进入绘制模式
  const drawMode = computed(() => mouse.value && mode.include(Mode.write))


  return reactive({
    viewMouseReact: mouse,
    viewMode,
    drawMode,
    mouseReact,
    editMode,
    dragMode,
  });
});

export const usePointerPos = installGlobalVar(() => {
  const stage = useStage();
  const pos = ref<Pos | null>(null);

  watchEffect((onCleanup) => {
    const $stage = stage.value?.getNode();
    if (!$stage) return;

    const mount = $stage.container().parentElement!;
    pos.value = $stage.pointerPos;
    onCleanup(
      listener(mount, "pointermove", () => {
        pos.value = $stage.pointerPos;
      })
    );
  });
  return pos;
}, Symbol("pointerPos"));


export const useDownKeys = installGlobalVar(() => {
  const keys = reactive(new Set<string>());
  const cleanup = mergeFuns(
    listener(window, "keydown", (ev) => {
      keys.add(ev.key);
    }),
    listener(window, "keyup", (ev) => {
      keys.delete(ev.key);
    })
  );
  return {
    var: keys,
    onDestroy: cleanup,
  };
});

export const useLayers = () => {
  const stage = useStage();
  return computed(() => stage.value?.getNode().children as Layer[]);
};

export const useTransformIngShapes = installGlobalVar(
  () => ref<EntityShape[]>([]),
  Symbol("transformIngShapes")
);


export const useCursor = installGlobalVar(
  () => stackVar('default'),
  Symbol('cursor')
)