<template>
  <div class="m-editor" ref="content" style="min-width: 900px">
    <slot name="header"></slot>

    <slot name="nav"></slot>

    <slot name="content-before"></slot>

    <slot name="src-code" v-if="showSrc">
      <CodeEditor
        class="m-editor-content"
        editor-custom-type="m-editor-content"
        :init-values="root"
        :options="codeOptions"
        @save="saveCode"
      ></CodeEditor>
    </slot>

    <SplitView
      v-show="!showSrc"
      ref="splitView"
      class="m-editor-content"
      left-class="m-editor-framework-left"
      center-class="m-editor-framework-center"
      right-class="m-editor-framework-right"
      :left="hideSidebar ? undefined : columnWidth.left"
      :right="columnWidth.right"
      :min-left="hideSidebar ? 0 : minLeftColumnWidth"
      :min-right="minRightColumnWidth"
      :min-center="minCenterColumnWidth"
      :width="frameworkRect.width"
      @change="columnWidthChange"
    >
      <template v-if="!hideSidebar" #left>
        <slot name="sidebar"></slot>
      </template>

      <template #center>
        <slot v-if="page" name="workspace"></slot>
        <slot v-else name="empty">
          <AddPageBox :disabled-page-fragment="disabledPageFragment"></AddPageBox>
        </slot>

        <slot name="page-bar">
          <PageBar
            :disabled-page-fragment="disabledPageFragment"
            :page-bar-sort-options="pageBarSortOptions"
            :filter-function="pageFilterFunction"
          >
            <template #page-bar-add-button><slot name="page-bar-add-button"></slot></template>
            <template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
            <template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
            <template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
          </PageBar>
        </slot>
      </template>

      <template v-if="page" #right>
        <slot name="props-panel"></slot>
      </template>
    </SplitView>

    <slot name="content-after"></slot>
    <slot name="footer"></slot>
  </div>
</template>

<script lang="ts" setup>
import { computed, inject, onBeforeUnmount, onMounted, useTemplateRef, watch } from 'vue';

import type { MPage, MPageFragment } from '@tmagic/core';

import SplitView from '@editor/components/SplitView.vue';
import { useServices } from '@editor/hooks/use-services';
import { Protocol } from '@editor/services/storage';
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config';
import {
  DEFAULT_LEFT_COLUMN_WIDTH,
  LEFT_COLUMN_WIDTH_STORAGE_KEY,
  RIGHT_COLUMN_WIDTH_STORAGE_KEY,
} from '@editor/utils/const';

import PageBar from './page-bar/PageBar.vue';
import AddPageBox from './AddPageBox.vue';
import CodeEditor from './CodeEditor.vue';

defineSlots<FrameworkSlots>();

defineOptions({
  name: 'MEditorFramework',
});

const props = defineProps<{
  disabledPageFragment: boolean;
  pageBarSortOptions?: PageBarSortOptions;
  pageFilterFunction?: (_page: MPage | MPageFragment, _keyword: string) => boolean;
  /** 是否隐藏左侧面板 */
  hideSidebar?: boolean;
}>();

const codeOptions = inject('codeOptions', {});
const { editorService, uiService, storageService } = useServices();

const contentEl = useTemplateRef<HTMLDivElement>('content');
const splitViewRef = useTemplateRef<InstanceType<typeof SplitView>>('splitView');

const root = computed(() => editorService.get('root'));
const page = computed(() => editorService.get('page'));

const pageLength = computed(() => editorService.get('pageLength') || 0);
const showSrc = computed(() => uiService.get('showSrc'));

const columnWidth = computed(() => uiService.get('columnWidth'));

const minLeftColumnWidth = computed(() => uiService.get('minLeftColumnWidth'));
const minCenterColumnWidth = computed(() => uiService.get('minCenterColumnWidth'));
const minRightColumnWidth = computed(() => uiService.get('minRightColumnWidth'));

watch(pageLength, () => {
  splitViewRef.value?.updateWidth();
});

watch(
  () => uiService.get('hideSlideBar'),
  (hideSlideBar) => {
    uiService.set('columnWidth', {
      ...columnWidth.value,
      left: hideSlideBar
        ? 0
        : storageService.getItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, { protocol: Protocol.NUMBER }) ||
          DEFAULT_LEFT_COLUMN_WIDTH,
    });
  },
);

const columnWidthChange = (columnW: GetColumnWidth) => {
  // 隐藏左侧面板时 left 恒为 0，不覆盖已持久化的左栏宽度，避免重新展示时丢失宽度
  if (!props.hideSidebar) {
    storageService.setItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, columnW.left, { protocol: Protocol.NUMBER });
  }
  storageService.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, columnW.right, { protocol: Protocol.NUMBER });

  uiService.set('columnWidth', columnW);
};

const frameworkRect = computed(() => uiService.get('frameworkRect'));

const resizerObserver = new ResizeObserver((entries) => {
  const { contentRect } = entries[0];
  uiService.set('frameworkRect', {
    width: contentRect.width,
    height: contentRect.height,
    left: contentRect.left,
    top: contentRect.top,
  });
});

onMounted(() => {
  if (contentEl.value) {
    resizerObserver.observe(contentEl.value);
  }
});

onBeforeUnmount(() => {
  resizerObserver.disconnect();
});

const saveCode = (value: string) => {
  try {
    const parseDSL = getEditorConfig('parseDSL');
    editorService.set('root', parseDSL(value), { historySource: 'root-code' });
  } catch (e: any) {
    console.error(e);
  }
};
</script>
