<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 })"
      @add-path="(preset) => add('paths', { reverse: false, ...preset })"
      @add-subtitle="add('subtitles', { content: '', background: '#000000' })"
      @add-action="(preset) => add('actions', preset)"
      @apply-global="k => ams.forEach((am: any) => (am[k] = focusAM![k]))"
    />
    <GlobalFrame
      class="global-frame"
      v-if="activeAttrib?.key !== 'frames' && !play"
      :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 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 } from "@/components/drawing-time-line/check";
import { Message } from "bill/expose-common";
import { mergeFuns, uuid } from "@/components/drawing/hook";
import { title } from "./type";
import { amMap, getAMKey, currentTime, play } from "@/sdk/association/animation";
import { sdk, AnimationModel3D } from "@/sdk";
import {
  showBottomBarStack,
  showLeftCtrlPanoStack,
  showLeftPanoStack,
  showRightPanoStack,
  showSearchStack,
} from "@/env";
import { clickListener } from "@/utils/event";
import { useRMenus } from "@/components/right-menu";

enterEdit(() => router.back());
useViewStack(autoSaveAnimationModel);
sysBus.on("leave", () => {
  updateFocus();
});
useViewStack(() => {
  const showRight = ref(false);
  return mergeFuns(
    showBottomBarStack.push(ref(true)),
    showRightPanoStack.push(showRight),
    showLeftPanoStack.push(ref(true)),
    showLeftCtrlPanoStack.push(computed(() => !play.value)),
    showSearchStack.push(computed(() => !play.value)),
    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 amM = computed(() => focusAM.value && amMap[getAMKey(focusAM.value)]);

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) => {
    if (!data.byControl) return;
    if (activeAttrib.value?.key === "frames") {
      const frame = focusAM.value!.frames[activeAttrib.value.ndx];
      frame.mat = JSON.parse(JSON.stringify(am3d.getModelPose()));
    } else {
      focusAM.value!.mat = JSON.parse(JSON.stringify(am3d.getModelPose()));
    }
  };

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

  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) {
    asyncOper(focusAM.value, (item) => {
      item.changeSelect(false);
    });
  }
  activeAttrib.value = undefined;
  focusAM.value = am;
  am && asyncOper(am, (item) => item.changeSelect(true));
};

watchEffect((onCleanup) => {
  onCleanup(
    mergeFuns(
      ams.value.map((item) => {
        return asyncOper(item, (s) =>
          s.bus.on("changeSelect", (f) => {
            if (f) {
              focusAM.value = item;
            } else if (focusAM.value === item) {
              focusAM.value = undefined;
            }
          })
        );
      })
    )
  );
});

watch(activeAttrib, (_a, _b, onCleanup) => {
  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" }
    )
  );
});

const add = <T extends Active["key"]>(
  key: T,
  preset: Partial<AnimationModel[T][0]> = {}
) => {
  const attr = getAddTLItemAttr(
    focusAM.value![key],
    currentTime.value,
    preset.duration || 10,
    1
  );
  if (!attr) {
    Message.error("当前时间已存在其他" + title[key]);
  } else {
    const item = reactive({
      id: uuid(),
      name: title[key],
      ...attr,
      ...preset,
    } as any);
    if (key === "frames") {
      asyncOper(
        focusAM.value!,
        (am3d) => (item.mat = JSON.parse(JSON.stringify(am3d.getModelPose())))
      );
    }
    focusAM.value![key].push(item);
    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) {
          focusAM.value = 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(
  clickListener(
    document.querySelector("#layout-app") 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
  )
);
</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>
