<template>
  <div :class="{ focusAM: focusAM }" class="animation-layout">
    <Left
      :focus="focusAM"
      @update:focus="updateFocus"
      class="animation-left"
      @change-select="changeSelect"
      @delete="deleteAm"
    />
    <Right
      v-if="focusAM && !play"
      :am="focusAM"
      :frameAction="frameAction"
      @change-frame-action="(f) => (frameAction = f.action)"
      class="animation-right"
      v-model:activeAttrib="activeAttrib"
      @add-frame="add('frames', { duration: 0.1 })"
      @add-path="(preset) => add('paths', { reverse: false, ...preset })"
      @add-subtitle="add('subtitles', { content: '', background: '#000000' })"
      @add-action="(preset) => add('actions', preset)"
      @apply-global="(k) => applyGlobal(k)"
    />
    <GlobalFrame
      class="global-frame"
      v-if="activeAttrib?.key !== 'frames' && !play && focusAM && !currentIsFullView"
      :data="{ id: '0', name: 'global-frame', time: 0 }"
      :frame-action="frameAction"
      @change-frame-action="(action) => (frameAction = action.action)"
    />

    <BottomPano class="strengthen-top">
      <Bottom
        :am="focusAM"
        :follow="play || follow"
        v-model:current-time="currentTime"
        v-model:active="activeAttrib"
      />
    </BottomPano>
  </div>
</template>

<script lang="ts" setup>
import Left from "./left.vue";
import Right from "./right/index.vue";
import GlobalFrame from "./right/frame.vue";
import Bottom from "./bottom.vue";
import BottomPano from "@/layout/bottom-pano.vue";
import router, { back } from "@/router";
import { enterEdit, sysBus } from "@/store";
import { useViewStack } from "@/hook";
import { ams, AnimationModel, autoSaveAnimationModel } from "@/store/animation";
import { computed, nextTick, onUnmounted, reactive, ref, watch, watchEffect } from "vue";
import { Active } from "./type";
import {
  getAddTLItemAttr,
  getAddTLItemTimeByTime,
} from "@/components/drawing-time-line/check";
import { Dialog, Message } from "bill/expose-common";
import { listener, mergeFuns, uuid } from "@/components/drawing/hook";
import { title } from "./type";
import {
  amMap,
  getAMKey,
  currentTime,
  play,
  animationGroup,
} from "@/sdk/association/animation";
import { sdk, AnimationModel3D } from "@/sdk";
import {
  bottomBarHeightStack,
  showAMsStack,
  showBottomBarStack,
  showHeadBarStack,
  showLeftCtrlPanoStack,
  showLeftPanoStack,
  showModeTabStack,
  showRightPanoStack,
  showSearchStack,
} from "@/env";
import { clickListener } from "@/utils/event";
import { useRMenus } from "@/components/right-menu";
import { asyncTimeout } from "@/utils";
import { currentIsFullView } from "@/utils/full";

enterEdit(() => back());
useViewStack(autoSaveAnimationModel);
sysBus.on("leave", () => {
  updateFocus();
});
useViewStack(() => {
  const showRight = ref(false);
  animationGroup.enterEditPannel();
  return mergeFuns(
    showBottomBarStack.push(ref(true)),
    bottomBarHeightStack.push(ref("0px")),
    showRightPanoStack.push(showRight),
    showLeftPanoStack.push(ref(true)),
    showAMsStack.push(ref(true)),
    showHeadBarStack.push(computed(() => !play.value)),
    showLeftCtrlPanoStack.push(computed(() => !play.value)),
    showModeTabStack.push(computed(() => !play.value)),
    showSearchStack.push(computed(() => !play.value)),
    () => animationGroup.exitEditPannel(),
    watchEffect((onCleanup) => {
      const cleanups: any[] = [];
      if (play.value || activeAttrib.value?.key === "frames") {
        cleanups.push(showRightPanoStack.push(ref(false)));
      }
      if (play.value) {
        cleanups.push(showLeftPanoStack.push(ref(false)));
      }
      onCleanup(mergeFuns(cleanups));
    }),
    watchEffect(() => {
      showRight.value = !!focusAM.value;
    })
  );
});

const focusAM = ref<AnimationModel>();
const activeAttrib = ref<Active>();

const follow = ref(false);
const frameAction = ref<string>();

const applyGlobal = async (k: keyof AnimationModel) => {
  console.error(k, focusAM.value![k]);
  if (!(await Dialog.confirm("确定要将此属性应用到所有动画模型？"))) return;
  ams.value.forEach((am: any) => (am[k] = focusAM.value![k]));
};

const amM = computed(() => focusAM.value && amMap[getAMKey(focusAM.value)]);
watch(play, (play, _, onCleanup) => {
  if (play) {
    const oldAction = frameAction.value;
    frameAction.value = undefined;
    onCleanup(() => (frameAction.value = oldAction));
  }
});
onUnmounted(() => {
  currentTime.value = 0;
  play.value = false;
});

const asyncOper = (item: AnimationModel, oper: (obj: AnimationModel3D) => void) => {
  let onCleanup = () => {};
  if (amMap[getAMKey(item)]?.am) {
    oper(amMap[getAMKey(item)]!.am!);
  } else {
    onCleanup = watchEffect(() => {
      if (amMap[getAMKey(item)]?.am!) {
        oper(amMap[getAMKey(item)]!.am!);
        onCleanup && onCleanup();
      }
    });
  }
  return onCleanup;
};

watchEffect((onCleanup) => {
  if (!amM.value) return;
  const am3d = amM.value.am;
  if (!am3d) return;

  const updateMat = (data: any) => {
    console.log("0.0.0", data);
    if (!data.byControl) return;
    const mat = JSON.parse(JSON.stringify(am3d.getModelPose()));
    if (data.quaAtPath) {
      focusAM.value!.mat = !focusAM.value!.mat
        ? { quaAtPath: data.quaAtPath }
        : {
            ...focusAM.value!.mat,
            quaAtPath: data.quaAtPath,
          };
    } else if (activeAttrib.value?.key === "frames") {
      const frame = focusAM.value!.frames[activeAttrib.value.ndx];
      frame.mat = mat;
    } else {
      focusAM.value!.mat = !focusAM.value!.mat
        ? mat
        : {
            quaAtPath: focusAM.value!.mat.quaAtPath,
            ...mat,
          };
    }
  };

  am3d.bus.on("transformChanged", updateMat);

  console.error(frameAction.value, am3d);
  switch (frameAction.value) {
    case "translate":
      am3d.enterMoveMode();
      break;
    case "rotate":
      am3d.enterRotateMode();
      break;
    case "scale":
      am3d.enterScaleMode();
      break;
  }
  onCleanup(() => {
    am3d.leaveTransform();
    am3d.bus.off("transformChanged", updateMat);
  });
});

const updateFocus = (am?: AnimationModel) => {
  if (focusAM.value && focusAM.value !== am) {
    asyncOper(focusAM.value, (item) => {
      item.changeSelect(false);
    });
  }
  activeAttrib.value = undefined;
  focusAM.value = am;
  am && asyncOper(am, (item) => item.changeSelect(true));
};
watch(
  () => [ams.value, ams.value.map((am) => amMap[getAMKey(am)]?.am)] as const,
  ([ams, am3ds], _, onCleanup) => {
    const cleanups = am3ds.map((am3d, ndx) => {
      const am = ams[ndx];
      console.log("监听", am.title, am3d);
      const update = (f: boolean) => {
        console.log("changeSelect", f);
        if (focusAM.value === am) {
          focusAM.value = f ? am : undefined;
        } else if (f) {
          focusAM.value = am;
        }
      };
      am3d?.bus.on("changeSelect", update);

      return () => {
        console.log("取消监听", am.title, am3d);
        am3d?.bus.off("changeSelect", update);
      };
    });
    onCleanup(mergeFuns(cleanups));
  },
  { immediate: true }
);

watch(
  activeAttrib,
  async (_a, _b, onCleanup) => {
    console.error("activeAttrib", activeAttrib.value);
    if (!activeAttrib.value) return;
    const cur = focusAM.value![activeAttrib.value.key][activeAttrib.value.ndx];
    const updateFocus = () => {
      const rang = [cur.time, cur.time + (cur.duration || 0)];

      if (currentTime.value < rang[0] || currentTime.value > rang[1]) {
        activeAttrib.value = undefined;
      }
    };

    follow.value = true;
    currentTime.value = cur.time!;
    nextTick(() => (follow.value = false));
    onCleanup(
      watch(
        () => [currentTime.value, cur.time, cur.time + (cur.duration || 0)],
        updateFocus,
        { flush: "sync" }
      )
    );
  },
  { flush: "sync" }
);

const add = <T extends Active["key"]>(
  key: T,
  preset: Partial<AnimationModel[T][0]> = {}
) => {
  const attr = getAddTLItemTimeByTime(
    focusAM.value![key],
    currentTime.value,
    typeof preset.duration === "number" ? preset.duration : 10
  );
  if (!attr) {
    Message.error("同一时间内请勿重复添加");
  } else {
    const item = reactive({
      id: uuid(),
      name: title[key],
      ...preset,
      ...attr,
    } as any);
    if (key === "frames") {
      asyncOper(
        focusAM.value!,
        (am3d) => (item.mat = JSON.parse(JSON.stringify(am3d.getModelPose())))
      );
    }
    focusAM.value![key].push(item);
    // currentTime.value = item.time;
    activeAttrib.value = {
      ndx: focusAM.value![key].length - 1,
      key,
    };
  }
};

let cleanSelect: (() => void) | null = null;
const changeSelect = ({ select, unSelect }: Record<string, AnimationModel[]>) => {
  cleanSelect && cleanSelect();
  cleanSelect = mergeFuns(
    ...select.map((item) => asyncOper(item, (am) => am.changeShow(true))),
    ...unSelect.map((item) =>
      asyncOper(item, (am) => {
        if (item === focusAM.value) {
          updateFocus(undefined);
        }
        am.changeShow(false);
      })
    )
  );
};

const deleteAm = (am: AnimationModel) => {
  if (am === focusAM.value) {
    activeAttrib.value = undefined;
    focusAM.value = undefined;
  }
  ams.value.splice(ams.value.indexOf(am), 1);
};

let unMount: () => void;
onUnmounted(
  mergeFuns([
    // listener(
    //   document.querySelector("#layout-app .scene-canvas") as HTMLDivElement,
    //   "contextmenu",
    //   (ev) => {
    //     ev.preventDefault();
    //   }
    // ),
    clickListener(
      document.querySelector("#layout-app .scene-canvas") as HTMLDivElement,
      (pixel) => {
        const pos = sdk.getPositionByScreen(pixel);
        if (!focusAM.value) return;
        unMount && unMount();
        setTimeout(() => {
          unMount = useRMenus(pixel, [
            {
              label: "移动到这里",
              icon: "move",
              handler() {
                amMap[getAMKey(focusAM.value!)]?.am?.moveModelTo(pixel, pos?.worldPos);
              },
            },
          ]);
        });
      },
      2
    ),
  ])
);
frameAction.value = "translate";
</script>

<style lang="scss" scoped>
.animation-layout {
  --bottom-height: 70px;

  &.focusAM {
    --bottom-height: 225px;
  }
}

.animation-left {
  height: calc(100vh - var(--bottom-height));
  position: absolute;
  width: var(--left-pano-width);
}

.animation-right {
  height: calc(100vh - var(--bottom-height));
  padding-top: 0;
  position: absolute;
  right: 0;
  top: 0;
}
</style>
<style>
.animation-left .left-pano {
  bottom: 0 !important;
}
</style>
