<template>
  <FlexRow position="relative" align="center">
    <slot name="addonBefore">
      <template v-if="addonBefore">
        <text>{{ addonBefore }}</text>
        <Width :width="prefixTextSpace" />
      </template>
    </slot>
    <slot name="minus" :disabled="disabled || value <= min" :onClick="minus">
      <IconButton
        :icon="minusIcon"
        :disabled="disabled || value <= min"
        :color="buttonColor"
        :buttonStyle="{ 
          ...themeStyles.button.value,
          ...selectObjectByType(size, 'medium', {
            small: themeStyles.buttonSmall.value,
            medium: themeStyles.buttonMedium.value,
            large: themeStyles.buttonLarge.value,
          }),
          ...buttonStyle,
        }"
        :padding="5"
        :size="iconSize"
        @click="minus"
      />
    </slot>
    <slot 
      name="center"
      :disableInput="disableInput"
      :value="stringValue"
      :integer="integer",
      :onChangeText="onChangeText"
      :onBlur="onTextBlur"
      :editable="!disabled"
    >
      <view :style="{
        ...themeStyles.inputWrapper.value,
        ...selectObjectByType(size, 'medium', {
          small: themeStyles.inputWrapperSmall.value,
          medium: themeStyles.inputWrapperMedium.value,
          large: themeStyles.inputWrapperLarge.value,
        }),
        ...inputStyle,
      }">
        <input
          :style="{
            ...themeStyles.input.value,
            width: themeContext.resolveThemeSize(inputWidth),
            color: themeContext.resolveThemeColor(textColor)
          }"
          :disabled="disabled || disableInput"
          :value="stringValue"
          :placeholder="placeholder"
          :placeholder-style="`color: ${themeContext.resolveThemeColor(placeholderTextColor)}`"
          type="number"
          confirm-type="done"
          @input="(e: any) => onChangeText(e.detail.value)"
          @blur="onTextBlur"
          @confirm="onTextBlur"
        />
      </view>
    </slot>
    <slot name="add" :disabled="disabled || !max || value >= max" :onClick="add">
      <IconButton
        :icon="addIcon"
        :disabled="disabled || (max ? value >= max : undefined)"
        :color="buttonColor"
        :buttonStyle="{ 
          ...themeStyles.button.value,
          ...selectObjectByType(size, 'medium', {
            small: themeStyles.buttonSmall.value,
            medium: themeStyles.buttonMedium.value,
            large: themeStyles.buttonLarge.value,
          }),
          ...buttonStyle,
        }"
        :padding="5"
        :size="iconSize"
        @click="add"
      />
    </slot>
    <slot name="addonAfter">
      <template v-if="addonAfter">
        <Width :width="prefixTextSpace" />
        <text>{{ addonAfter }}</text>
      </template>
    </slot>
  </FlexRow>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, toRef, watch } from 'vue';
import IconButton from '../basic/IconButton.vue';
import FlexRow from '../layout/FlexRow.vue';
import { propGetThemeVar, useTheme } from '../theme/ThemeDefine';
import { StringUtils } from '@imengyu/imengyu-utils';
import { DynamicColor, DynamicSize, selectObjectByType } from '../theme/ThemeTools';
import { useFieldChildValueInjector } from './FormContext';
import Width from '../layout/space/Width.vue';

export interface StepperProps {
  /**
   * 当前数值
   * @default 0
   */
  modelValue?: number;
  /**
   * 最大值，为空无限制
   * @default undefined
   */
  max?: number;
  /**
   * 最小值
   * @default 1
   */
  min?: number;
  /**
   * 中间输入框的宽度
   * @default 50
   */
  inputWidth?: number;
  /**
   * 组件大小
   * @default 'medium'
   */
  size?: 'small' | 'medium' | 'large';
  /**
   * 步长，每次点击时改变的值
   * @default 1
   */
  step?: number;
  /**
   * 初始值，当 value 为空时生效
   * @default 0
   */
  defaultValue?: number;
  /**
   * 固定显示的小数位数
   * @default 0
   */
  decimalLength?: number;
  /**
   * 是否禁用
   * @default false
   */
  disabled?: boolean;
  /**
   * 是否禁用输入框
   * @default false
   */
  disableInput?: boolean;
  /**
   * 是否只允许输入整数
   * @default false
   */
  integer?: boolean;
  /**
   * 输入框的文本颜色
   */
  textColor?: string,
  /**
   * 输入框的占位符
   */
  placeholder?: string,
  /**
   * 输入框的占位符颜色
   */
  placeholderTextColor?: string,
  /**
   * 加号图标名称
   * @default 'add-bold'
   */
  addIcon?: string;
  /**
   * 键号图标名称
   * @default 'minus-bold'
   */
  minusIcon?: string;
  /**
   * 组件左侧添加的文本
   */
  addonBefore?: string,
  /**
   * 组件右侧添加的文本
   */
  addonAfter?: string,
  /**
   * 按钮颜色
   */
  buttonColor?: string,
  /**
   * 自定义按钮样式
   */
  buttonStyle?: Record<string, any>,
  /**
   * 自定义输入框样式
   */
  inputStyle?: Record<string, any>,
}

const emit = defineEmits([ 'update:modelValue' ])

const props = withDefaults(defineProps<StepperProps>(), {
  modelValue: 0,
  max: undefined,
  min: 1,
  inputWidth: () => propGetThemeVar('StepperInputWidth', 100),
  step: 1,
  defaultValue: 0,
  decimalLength: 0,
  disabled: false,
  disableInput: false,
  integer: false,
  addIcon: () => propGetThemeVar('StepperAddIcon', 'add-bold'),
  minusIcon: () => propGetThemeVar('StepperMinusIcon', 'minus-bold'),
});

const themeContext = useTheme();
const themeStyles = themeContext.useThemeStyles({
  button: {
    borderRadius: DynamicSize('StepperButtonBorderRadius', 0),
    backgroundColor: DynamicColor('StepperButtonBackgroundColor', 'light'),
  },
  buttonLarge: {
    borderRadius: DynamicSize('StepperButtonBorderRadius', 0),
    paddingTop: DynamicSize('StepperButtonPaddingVertical', 8),
    paddingBottom: DynamicSize('StepperButtonPaddingVertical', 8),
    paddingLeft: DynamicSize('StepperButtonPaddingHorizontal', 8),
    paddingRight: DynamicSize('StepperButtonPaddingHorizontal', 8),
    backgroundColor: DynamicColor('StepperButtonBackgroundColor', 'light'),
  },
  buttonMedium: {
    paddingTop: DynamicSize('StepperButtonPaddingVertical', 4),
    paddingBottom: DynamicSize('StepperButtonPaddingVertical', 4),
    paddingLeft: DynamicSize('StepperButtonPaddingHorizontal', 4),
    paddingRight: DynamicSize('StepperButtonPaddingHorizontal', 4),
  },
  buttonSmall: {
    borderRadius: DynamicSize('StepperButtonBorderRadius', 0),
    paddingTop: DynamicSize('StepperButtonPaddingVertical', 2),
    paddingBottom: DynamicSize('StepperButtonPaddingVertical', 2),
    paddingLeft: DynamicSize('StepperButtonPaddingHorizontal', 2),
    paddingRight: DynamicSize('StepperButtonPaddingHorizontal', 2),
  },
  inputWrapper: {
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    alignSelf: 'stretch',
    backgroundColor: DynamicColor('StepperInputBackgroundColor', 'light'),
  },
  inputWrapperSmall: {
    paddingTop: DynamicSize('StepperInputPaddingVertical', 0),
    paddingBottom: DynamicSize('StepperInputPaddingVertical', 0),
    paddingLeft: DynamicSize('StepperInputPaddingHorizontal', 6),
    paddingRight: DynamicSize('StepperInputPaddingHorizontal', 6),
    marginLeft: DynamicSize('StepperInputMarginHorizontal', 2),
    marginRight: DynamicSize('StepperInputMarginHorizontal', 2),
    marginTop: DynamicSize('StepperInputMarginVertical', 0),
    marginBottom: DynamicSize('StepperInputMarginVertical', 0),
  },
  inputWrapperMedium: {
    paddingTop: DynamicSize('StepperInputPaddingVertical', 0),
    paddingBottom: DynamicSize('StepperInputPaddingVertical', 0),
    paddingLeft: DynamicSize('StepperInputPaddingHorizontal', 10),
    paddingRight: DynamicSize('StepperInputPaddingHorizontal', 10),
    marginLeft: DynamicSize('StepperInputMarginHorizontal', 4),
    marginRight: DynamicSize('StepperInputMarginHorizontal', 4),
    marginTop: DynamicSize('StepperInputMarginVertical', 0),
    marginBottom: DynamicSize('StepperInputMarginVertical', 0),
  },
  inputWrapperLarge: {
    paddingTop: DynamicSize('StepperInputPaddingVertical', 0),
    paddingBottom: DynamicSize('StepperInputPaddingVertical', 0),
    paddingLeft: DynamicSize('StepperInputPaddingHorizontal', 20),
    paddingRight: DynamicSize('StepperInputPaddingHorizontal', 20),
    marginLeft: DynamicSize('StepperInputMarginHorizontal', 8),
    marginRight: DynamicSize('StepperInputMarginHorizontal', 8),
    marginTop: DynamicSize('StepperInputMarginVertical', 0),
    marginBottom: DynamicSize('StepperInputMarginVertical', 0),
  },
  input: {
    textAlign: 'center',
    color: DynamicColor('StepperInputTextColor', 'text.content'),
  },
});
const prefixTextSpace = computed(() => themeContext.resolveThemeSize('StepperPrefixTextSpace', 12));
const iconSize = computed(() => {
  switch (props.size) {
    case 'small':
      return themeContext.resolveThemeSize('StepperIconSizeSmall', 24);
    default:
    case 'medium':
      return themeContext.resolveThemeSize('StepperIconSizeMedium', 36);
    case 'large':
      return themeContext.resolveThemeSize('StepperIconSizeLarge', 48);
  }
});

const {
  value,
  updateValue,
} = useFieldChildValueInjector(
  toRef(props, 'modelValue'), 
  (v) => emit('update:modelValue', v)
);

const stringValue = ref('');

watch(() => [ value.value, props.decimalLength, props.integer ], () => {
  const v = value.value, d = props.decimalLength, i = props.integer;
  //数据更改时更新文字
  stringValue.value = ((i || d === 0) ? v.toString() : v.toFixed(d));
})

function setTextString(v: number) {
  stringValue.value = ((props.integer || props.decimalLength === 0) ? Math.floor(v).toString() : v.toFixed(props.decimalLength));
}
function onChangeText(v: string) {
  stringValue.value = v;
}
function onTextBlur() {
  const s = stringValue.value;

  //输入框失去焦点后校验数值，用户可以从输入框直接输入数据
  if (StringUtils.isNullOrEmpty(s) || !StringUtils.isNumber(s)) {
    setTextString(props.defaultValue);
    emitChange(props.defaultValue);
    return;
  }

  let newValue = props.integer ? parseInt(s, 10) : parseFloat(s);
  if (newValue < props.min )
    newValue = props.min;
  if (props.max && newValue > props.max)
    newValue = props.max;

  setTextString(newValue);
  emitChange(newValue);
}
function emitChange(newValue: number) {
  updateValue(newValue);
}
function add() {
  //加
  let newValue = value.value + props.step;
  if (props.max && newValue > props.max)
    newValue = props.max;
  emitChange(newValue);
}
function minus() {
  //键
  let newValue = value.value - props.step;
  if (newValue < props.min )
    newValue = props.min;
  emitChange(newValue);
}
onMounted(() => {
  setTextString(props.modelValue);
})

</script>