<template>
  <!-- :style="{ width: svgAttrib.viewBox[2] + 'px', height: svgAttrib.viewBox[3] + 'px' }" -->

  <svg
    :viewBox="svgAttrib.viewBox.join(' ')"
    v-if="svgAttrib"
    xmlns="http://www.w3.org/2000/svg"
  >
    <Step
      v-for="step in steps"
      :key="step.id"
      v-bind="getStepAttrib(step)"
      @click="emit('stepClick', step)"
      @click-host="(data) => emit('stepHostClick', data)"
    />
  </svg>
</template>

<script lang="ts" setup>
import { computed } from "vue";
import { flatSteps, attachBoundAttrib, NStep, getTextBound } from "./helper";
import Step from "./step.vue";

const props = withDefaults(
  defineProps<{
    margin?: number[];
    padding?: number[];
    fontSize?: number;
    data: any;
    fontFamily?: string;
    hostFontSize?: number;
    hostMargin?: number[];
    hostPadding?: number[];
    customStepStyle?: (step: any) => {};
    lineGap: number;
  }>(),
  {
    margin: () => [10, 10],
    padding: () => [10, 10],
    hostMargin: () => [2, 2],
    hostPadding: () => [2, 2],
    hostFontSize: 10,
    lineGap: 5,
    fontSize: 14,
    fontFamily: "sans-serif",
  }
);

const emit = defineEmits<{
  (e: "stepClick", data: any): void;
  (e: "stepHostClick", host: any): void;
}>();

const getStepSize = (step: any) => {
  const size = getTextBound(
    step.name,
    props.padding,
    props.margin,
    `${props.fontSize}px normal ${props.fontFamily}`
  );

  if (step.hosts?.length) {
    const hostsGroup = [];
    const numGroup = 1;
    for (let i = 0; i < step.hosts.length; i += numGroup) {
      hostsGroup.push(step.hosts.slice(i, i + numGroup));
    }
    let top = 0;
    const hostSizeGroup = hostsGroup.map((hosts) => {
      let left = 0;
      const hostSize = hosts.reduce(
        (t: any, host: any) => {
          const size = getTextBound(
            host.host,
            props.hostPadding,
            props.hostMargin,
            `${props.hostFontSize}px normal ${props.fontFamily}`
          );
          t.width += size.width;
          t.height = Math.max(t.height, size.height);
          host.bound = {
            ...size,
            left,
            top,
          };
          left += size.width;
          return t;
        },
        { width: 0, height: 0 }
      );
      top += hostSize.height;
      return hostSize;
    });

    const hostSize = hostSizeGroup.reduceRight(
      (t, hostSize) => {
        t.width = Math.max(hostSize.width, t.width);
        t.height += hostSize.height;
        return t;
      },
      { width: 0, height: 0 }
    );
    step.hostSize = hostSize;
    size.width = Math.max(
      size.width,
      hostSize.width + (props.padding[1] + props.margin[1]) * 2
    );
    size.height += hostSize.height;
  }

  return size;
};

const steps = computed(() => flatSteps(props.data));
const bound = computed(() => attachBoundAttrib(steps.value, getStepSize));

const svgAttrib = computed(() => {
  if (!bound.value) return null;
  const { left, right, top, bottom } = bound.value;
  return {
    viewBox: [left, top, right - left, bottom - top],
  };
});

const lineGap = computed(() => Math.min(props.lineGap, props.margin[0]));

const getStepLines = (step: NStep) => {
  if (!step.parentIds.length) return [];
  const start = [
    step.bound.left + step.bound.width / 2,
    step.bound.top + props.margin[0],
  ];
  const points = [];
  for (let parentId of step.parentIds) {
    const parent = steps.value.find((step) => step.id === parentId)!;
    const end = [
      parent.bound.left + parent.bound.width / 2,
      parent.bound.top + parent.bound.height - props.margin[0],
    ];
    const startLevelHeight = bound.value.levelHeights[step.level];
    // const parentLevelHeight = bound.value.levelHeights[parent.level];
    const offset = lineGap.value + (startLevelHeight - step.bound.height) / 2;

    points.push([
      ...start,
      start[0],
      start[1] - offset,
      end[0],
      start[1] - offset,
      ...end,
    ]);
  }
  return points;
};

const defaultStyle = {
  lineColor: "#000",
  lineWidth: 1,
  textColor: "#000",
  rectBorderColor: "#000",
  rectBgColor: "#ffff",
  rectRadius: 2,
  rectBorderWidth: 1,
};

const getStepAttrib = (step: NStep) => {
  let style = defaultStyle;
  if (props.customStepStyle) {
    style = { ...defaultStyle, ...props.customStepStyle(step.raw) };
  }
  return {
    style,
    step,
    margin: props.margin,
    padding: props.padding,
    fontSize: props.fontSize,
    hostMargin: props.hostMargin,
    hostPadding: props.hostPadding,
    hostFontSize: props.hostFontSize,
    fontFamily: props.fontFamily,
    lines: getStepLines(step),
  };
};
</script>
