<template>
  <text :id="id" :class="innerClass" :style="style" @click="onClick">
    <!-- #ifdef APP-NVUE -->
    {{ text }}
    <!-- #endif -->
    <!-- #ifndef APP-NVUE -->
    <slot>{{ text }}</slot>
    <!-- #endif -->
  </text>
</template>

<script lang="ts" setup>
import { computed, getCurrentInstance } from 'vue';
import { useTheme, type ThemePaddingMargin } from '../theme/ThemeDefine';
import { RandomUtils } from '@imengyu/imengyu-utils';

export interface TextProps {
  /**
   * 字体颜色。可以是颜色字符串或者在主题中配置的预设名称。
   */
  color?: string,
  /**
   * 文字阴影颜色。可以是颜色字符串或者在主题中配置的预设名称。
   */
  shadowColor?: string,
  /**
   * 文字对齐方式。
   */
  textAlign?: 'center'|'left'|'right'|'',
  /**
   * 字体预设。可以是主题中预设的一个名称。
   */
  fontConfig?: string,
  /**
   * 字体大小。可以是实际数值或者在主题中配置的预设名称。
   */
  fontSize?: string|number,
  /**
   * 字体名称。
   */
  fontFamily?: string,
  /**
   * 字体样式。
   */
  fontStyle?: string,
  /** 
   * 字体粗细。
   */
  fontWeight?: string|number,
  /**
   * 是否是粗体
   */
  bold?: boolean,
  /**
   * 是否是斜体
   */
  italic?: boolean,
  /**
   * 是否加下划线
   */
  underline?: boolean,
  /**
   * 是否加删除线
   */
  lineThrough?: boolean,
  /**
   * 是否有阴影。
   */
  shadow?: boolean,
  /**
   * 背景颜色。可以是颜色字符串或者在主题中配置的预设名称。
   */
  backgroundColor?: string, 
  /**
   * 是否可以选择
   */
  selectable?: boolean,
  /**
   * 是否允许换行
   * @default true
   */
  wrap?: boolean,
  /**
   * 行数限制
   */
  lines?: number,
  margin?: number|string|number[]|ThemePaddingMargin,
  padding?: number|string|number[]|ThemePaddingMargin,
  innerStyle?: object,
  innerClass?: object|string,
  /**
   * 最大宽度
   */
  maxWidth?: number|string,

  /**
   * 自动工具文字长短设置大小，在 autoSize 为 true 时有效。
   *
   * 这个是一个小功能，目的是为了在某些情况下（例如金额显示），容器宽度一定但是文字长短不定，
   * 此时需要自动缩放大小，文字越长，字号越小。
   *
   * 计算公式是 (1 - (text.length - minLen) / (maxLen - minLen)) * (maxSize - minSize) + minSize
   */
  autoSize?: {
    /**
     * 小程序无法获取模板内容，需要额外提供字符串
     */
    text?: string,
    /**
     * 最长文字长度，用于自动大小公式计算
     */
    maxLen: number;
    /**
     * 最短文字长度，如果输入文字长度小于这个值，则不会进行自动缩放
     */
    minLen: number;
    /**
     * 最大文字字号，用于自动大小公式计算
     */
    maxSize: number;
    /**
     * 最小文字字号，用于自动大小公式计算
     */
    minSize: number;
  },
  text?: string|number,
  /**
   * 是否可以点击
   */
  touchable?: boolean,
}

/**
 * 组件说明：文字封装，支持点击事件，颜色，阴影。
 */
const props = withDefaults(defineProps<TextProps>(), {
  shadowColor: '#000',
  bold: false,
  italic: false,
  underline: false,
  lineThrough: false,
  shadow: false,
  touchable: false,
  wrap: true,
});
const emit = defineEmits([ 'click' ])

const id = `text-${RandomUtils.genNonDuplicateID(12)}`;
const instance = getCurrentInstance();
const { resolveThemeColor, resolveThemeSize, getText } = useTheme();

function getAutoSize() {
  //自动缩放大小，文字越长，字号越小
  const autoSizeOption = props.autoSize;
  if (autoSizeOption) {
    const text = '' + autoSizeOption.text;
    if (text.length < autoSizeOption.minLen)
      return props.fontSize;
    return (1 - (text.length - autoSizeOption.minLen) / (autoSizeOption.maxLen - autoSizeOption.minLen))
      * (autoSizeOption.maxSize - autoSizeOption.minSize) + autoSizeOption.minSize;
  }
}
function onClick(e: any) {
  if (props.touchable) {
    emit("click", e)
  }
}

const style = computed(() => {
  const o : Record<string, any> = {
  }
  const style = props.fontConfig ? getText(props.fontConfig) : undefined;
  const color = props.color ?? style?.color as string;
  const backgroundColor = props.backgroundColor ?? style?.backgroundColor as string;
  const fontSize = props.fontSize ?? style?.fontSize as string;
  const fontWeight = props.fontWeight ?? style?.fontWeight as string;
  const fontFamily = props.fontFamily ?? style?.fontFamily as string;
  const fontStyle = props.fontStyle ?? style?.fontStyle as string;

  if (color) o.color = resolveThemeColor(color, "text");
  if (backgroundColor) o.background = resolveThemeColor(backgroundColor);
  if (fontSize) o.fontSize = resolveThemeSize(fontSize);
  if (fontWeight) o.fontWeight = fontWeight;
  if (fontStyle) o.fontStyle = fontStyle;
  if (!props.wrap) o.whiteSpace = 'nowrap';

  if (fontFamily) o.fontFamily = fontFamily;
  if (props.textAlign) {
    o.textAlign = props.textAlign;
  }
  if (props.selectable) o.userSelect = props.selectable;
  if (props.lines) {
    o.display = '-webkit-box';
		o['-webkit-line-clamp'] = props.lines;
		o['-webkit-box-orient'] = 'vertical';
		o.overflow = 'hidden';
    o.textOverflow = 'ellipsis';
  }
  if (props.bold)
    o.fontWeight = 'bold';
  if (props.italic) 
    o.fontStyle = 'italic';
  if ((props.underline || props.lineThrough) && !o.textDecoration)
    o.textDecoration = '';
  if (props.underline)
    o.textDecoration += ' underline';
  if (props.lineThrough)
    o.textDecoration += ' line-through';
  if (props.maxWidth)
    o.maxWidth = resolveThemeSize(props.maxWidth);

  if (props.shadow && props.shadowColor) {
    o.textShadow = '1px 1px 2px ' + resolveThemeColor(props.shadowColor, 'text');
  }
  if (props.autoSize) {
    const s = getAutoSize();
    if (s)
      o.fontSize = s + 'rpx';
  }

  const rs = {
    ...o,
    ...props.innerStyle
  };
  return rs;
})

async function measureTextWidth() {
  return await new Promise<number>((resolve) => {
    uni.createSelectorQuery()
      // #ifdef MP
      .in(instance)
      // #endif
      .select(`#${id}`)
      .boundingClientRect((data) => {
        resolve((data as UniApp.NodeInfo)?.width ?? 0)
      })
      .exec();
  })
}

defineExpose({
  measureTextWidth,
})
defineOptions({
  options: {
    virtualHost: true,
    styleIsolation: "shared",
  },
});

</script>

<style lang="scss">
.nana-text-can-press {
  cursor: pointer;
  opacity: 0.8;
}
</style>