<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"
      :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: '#fff' })"
      @add-action="(preset) => add('actions', preset)"
      @apply-global="k => ams.forEach((am: any) => (am[k] = focusAM![k]))"
    />
    <BottomPano>
      <Bottom
        :am="focusAM"
        v-model:follow="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 Bottom from "./bottom.vue";
import BottomPano from "@/layout/bottom-pano.vue";
import router from "@/router";
import { enterEdit } from "@/store";
import { useViewStack } from "@/hook";
import {
  ams,
  AnimationModel,
  autoSaveAnimationModel,
  initAnimationActions,
  initialAnimationModels,
} from "@/store/animation";
import { computed, nextTick, 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 } from "@/sdk/association/animation";
import { AnimationModel3D } from "@/sdk";
import { bottomBarHeightStack, showBottomBarStack } from "@/env";

enterEdit(() => router.back());
initialAnimationModels();
initAnimationActions();
useViewStack(autoSaveAnimationModel);
useViewStack(() => showBottomBarStack.push(ref(true)));

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)]);

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 || activeAttrib.value?.key !== "frames") return;
  const frame = focusAM.value!.frames[activeAttrib.value.ndx];
  const am3d = amM.value.am;
  if (!am3d) return;
  const updateMat = () => {
    frame.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)];

    console.log(rang, cur, cur.time);
    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
    )
  );
});

const add = <T extends Active["key"]>(
  key: T,
  preset: Partial<AnimationModel[T][0]> = {}
) => {
  const attr = getAddTLItemAttr(focusAM.value![key], currentTime.value, 10, 1);
  if (!attr) {
    Message.error("当前时间已存在其他" + title[key]);
  } else {
    const item = reactive({
      id: uuid(),
      name: title[key],
      ...attr,
      ...preset,
    } as any);
    if (key === "frames") {
      console.log(preset, item);
      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);
};
</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>
