import React from 'react';
import {
  OptionsControl,
  OptionsControlProps,
  highlight,
  FormOptionsControl
} from './Options';
import { Action } from '../../types';
import Downshift, { StateChangeOptions } from 'downshift';
import { matchSorter } from 'match-sorter';
import debouce from 'lodash/debounce';
import { filter } from '../../utils/tpl';
import find from 'lodash/find';
import { Icon } from '../../components/icons';
import Input from '../../components/Input';
import { autobind, createObject, focusInputAndChooseInput, getChineseWordLengthInWord, isMobile, setVariable, ucFirst } from '../../utils/helper';
import { isEffectiveApi } from '../../utils/api';
import Spinner from '../../components/Spinner';
import { FormBaseControl } from './Item';
import { ActionSchema } from '../Action';
import { SchemaApi } from '../../Schema';
import { generateIcon } from '../../utils/icon';

// Jay
import { message, Popover } from 'antd'
import Button from 'antd/lib/button'
import AntdInput from 'antd/lib/input';
import { HtmlProps } from '../../components/Html';
import { Shell } from '../../utils/shell';
import { EventEnum, EventSub } from '../../utils/sub';
import { EditOutlined } from '@ant-design/icons';
import TextareaPop from '../../components/common/TextareaPop';
import { isNil } from 'lodash';

// declare function matchSorter(items:Array<any>, input:any, options:any): Array<any>;

/**
 * Text 文本输入框。
 * 文档：https://baidu.gitee.io/amis/docs/components/form/text
 */
export interface TextControlSchema extends FormOptionsControl {
  type:
  | 'input-text'
  | 'input-email'
  | 'input-url'
  | 'input-password'
  | 'native-date'
  | 'native-time'
  | 'native-number';

  addOn?: {
    position?: 'left' | 'right';
    label?: string;
    icon?: string;
    className?: string;
  } & ActionSchema;



  /**
   * 是否去除首尾空白文本。
   */
  trimContents?: boolean;

  /**
   * 自动完成 API，当输入部分文字的时候，会将这些文字通过 ${term} 可以取到，发送给接口。
   * 接口可以返回匹配到的选项，帮助用户输入。
   */
  autoComplete?: SchemaApi;

  /**
   * 边框模式，全边框，还是半边框，或者没边框。
   */
  borderMode?: 'full' | 'half' | 'none';

  /**
   * 限制文字个数
   */
  maxLength?: number;

  /**
   * 是否显示计数
   */
  showCounter?: boolean;

  // Jay
  multipleValues?: boolean; // 多项值
  //将字符串转换成数字类型
  isNumber?: boolean;
}

let selectTimer = false

export interface TextProps extends OptionsControlProps {
  placeholder?: string;
  addOn?: Action & {
    position?: 'left' | 'right';
    label?: string;
    icon?: string;
    className?: string;
  };
  creatable?: boolean;
  clearable: boolean;
  resetValue?: any;
  autoComplete?: any;
  allowInputText?: boolean;
  spinnerClassName: string;
  transform?: {
    lowerCase?: boolean; // 用户输入的字符自动转小写
    upperCase?: boolean; // 用户输入的字符自动转大写
  };
  // Jay
  isMultipleValues?: boolean; // 多项值填入（xlsx单列复制到input框）
  keepcursor?: boolean; //按下回车是否保留光标 
  icon?: string;
  manual?: boolean;//键盘上下建控制寻找input-text
  showCount?: number,
  inputfocus?: boolean,
  htmlValue?: boolean // 从富文本传递
}

export interface TextState {
  isOpen?: boolean;
  inputValue?: string;
  defaultValue?: string;
  isFocused?: boolean;
  // Jay
  inputWidth?: string;
  popoverVisible?: boolean;
  popoverVisibleDefault?: boolean;
  multipleValues?: string;
  simulateSelectionPosition?: number | null | undefined;

  privateType?: 'password' | 'text' | '';
  outerStyle?: any;//动态计算下拉框的样式
}

export default class TextControl extends React.PureComponent<
  TextProps,
  TextState
> {
  input?: HTMLInputElement | null;
  popover: any
  popoverIconRef: React.RefObject<HTMLElement> // Jay
  intersection: {
    preIntersectionRatio?: number;
    curIntersectionRatio?: number;
    observer?: any
  } = {} // 元素可见性观察器
  highlightedIndex?: any;
  unHook: Function;
  showPasswordTimer: any // 展示密码文本的定时器
  sugsRef: React.RefObject<HTMLDivElement> = React.createRef();
  foucsTimer: any
  constructor(props: TextProps) {
    super(props);

    const value = props.value;
    // Jay
    let multipleValues = ''
    if (props.multiple || props.creatable === false) {

    } else {
      const { delimiter } = props
      const t = this.valueToString(value)
      multipleValues = t ? t + delimiter : ''
      multipleValues = multipleValues.replace(new RegExp(`${this.props.delimiter || ','}`, 'g'), '\n')
    }
    this.state = {
      privateType: this.props.keepcursor ? 'password' : '',
      simulateSelectionPosition: 0,
      isOpen: false,
      inputValue:
        props.multiple || props.creatable === false
          ? ''
          : this.valueToString(value),
      isFocused: false,
      // Jay
      popoverVisible: false,
      multipleValues,
      defaultValue: props.multiple || props.creatable === false
        ? ''
        : this.valueToString(value),
      popoverVisibleDefault: false,
      outerStyle: {}
    };
    this.focus = this.focus.bind(this);
    this.clearValue = this.clearValue.bind(this);
    this.inputRef = this.inputRef.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    this.addAutoFocus = this.addAutoFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleStateChange = this.handleStateChange.bind(this);
    this.handleKeyEnter = this.handleKeyEnter.bind(this);
    this.haneldText = this.haneldText.bind(this);
    this.loadAutoComplete = debouce(this.loadAutoComplete.bind(this), 250, {
      trailing: true,
      leading: false
    });
    this.popoverIconRef = React.createRef<HTMLElement>() // Jay

  }

  timer: ReturnType<typeof setTimeout>;
  static defaultProps: Partial<TextProps> = {
    resetValue: '',
    labelField: 'label',
    valueField: 'value',
    placeholder: '',
    allowInputText: true,
    trimContents: true,
    keepcursor: false,
    manual: true
  };

  componentDidMount() {
    const { formItem, autoComplete, addHook, formInited, data, name, keepcursor, disabled } =
      this.props;
    if (isEffectiveApi(autoComplete, data) && formItem) {
      if (formInited) {
        formItem.loadOptions(
          autoComplete,
          createObject(data, {
            term: ''
          })
        );
      } else {
        this.unHook = addHook(async (data: any) => {
          await formItem.loadOptions(
            autoComplete,
            createObject(data, {
              term: ''
            })
          );

          if (formItem.value) {
            setVariable(data, name!, formItem.value);
          }
        }, 'init');
      }
    }
    if (!disabled && keepcursor) {
      // 自动聚焦观察器
      // 如果需要保持focus状态 - 再元素第一次发生 不可见-> 可见状态时自动聚焦
      this.intersection.observer = new IntersectionObserver(([entry]) => {
        this.intersection.preIntersectionRatio = this.intersection.curIntersectionRatio || 0 // 默认值适用
        this.intersection.curIntersectionRatio = entry.intersectionRatio
        // 意为出现了部分元素聚焦 并且没有定时器 适用自动聚焦 
        if (this.intersection.preIntersectionRatio <= 0 && this.intersection.curIntersectionRatio > 0) {  // 如果需要持续聚焦，只要没有值就取消聚焦-可见状态才适用聚焦
          this.addAutoFocus()
        } else if (this.intersection.curIntersectionRatio <= 0) {
          // 看不到则取消定时器
          clearInterval(this.foucsTimer)
        }
      }, {
        root: document.body
      })


      // 观察器挂载 
      this.addAutoFocus() // 先加一个自动聚焦
      this.intersection.observer.observe(this.input)
      this.handleKeyEnter()
    }
  }

  addAutoFocus = () => {
    // 如果已经有定时器 或者非保持聚焦 不走这个逻辑
    if (this.foucsTimer) { // 清了原来的 换一个新的
      clearInterval(this.foucsTimer)
    }
    if (!this.props.keepcursor) return


    this.foucsTimer = setInterval(() => {
      // 如果没有其他聚焦 或者不是checkbox 或者 radio 类型 自动聚焦
      const focusInput: HTMLInputElement | null = document.querySelector(('*:focus'))
      const inputRect = this.input?.getBoundingClientRect() // input的计算大小
      // 如果input被其他元素遮住 阻止输入 返回 失焦
      if (document.elementFromPoint((inputRect?.x || 0) + (inputRect?.width || 0) / 2, (inputRect?.y || 0) + (inputRect?.height || 0) / 2) !== this.input) return
      // 如果有其他选中元素 失去焦点
      if (focusInput) return
      this.focus()
    }, 100)
    const clearListen = () => {
      clearInterval(this.foucsTimer)
      this.foucsTimer = null // 显式释放定时器
    }

    // // 点击其他地方自动移除-不立马添加 防止点击自己的时候也出这个问题
    // setTimeout(() => {
    //   document.addEventListener('click', clearListen, { once: true })
    // }, 100)
  }

  componentDidUpdate(prevProps: TextProps, prevState: TextState) {
    const props = this.props;
    if (props.keepcursor && props.inputfocus) {
      this.handleKeyEnter()
    }
    if (prevProps.value !== props.value) {
      this.setState({
        inputValue:
          props.multiple || props.creatable === false
            ? ''
            : this.valueToString(props.value),
        defaultValue: props.multiple || props.creatable === false
          ? ''
          : this.valueToString(props.value),
      });

      // Jay
      if (props.isMultipleValues) {
        let multipleValues = ''
        if (props.multiple || props.creatable === false) {
        } else {
          const { delimiter = ',' } = props
          const t = this.valueToString(props.value)
          multipleValues = t ? t + delimiter : ''
          multipleValues = multipleValues.replace(new RegExp(`${this.props.delimiter || ','}`, 'g'), '\n')
        }
        this.setState({ multipleValues })
      }

    }
    if (this.state.isOpen !== prevState.isOpen && this.state.isOpen) {
      const { classPrefix: ns } = props;
      //计算下拉框的位置
      let outerStyle = {}
      if (this.sugsRef.current && this.sugsRef.current.querySelector(`.${ns}TextControl-sugs`)) {
        const formControlData = this.sugsRef.current.getBoundingClientRect()
        const popData = this.sugsRef.current.querySelector(`.${ns}TextControl-sugs`) as HTMLDivElement;
        const isOverSize = document.documentElement.clientHeight - formControlData?.bottom! <= popData.clientHeight + 5
        if (this.sugsRef.current?.closest(`.${ns}Modal-body`)) {
          if (Shell.hasShell()) {
            outerStyle = {
              position: 'fixed',
              display: 'block',
              width: formControlData.width,
              top: isOverSize ? formControlData?.top! - popData.clientHeight : formControlData?.top! + formControlData?.height!,
              left: formControlData?.left!
            }
          } else {
            const modalData = this.sugsRef.current.closest(`.${ns}Modal`)?.getBoundingClientRect()
            outerStyle = {
              position: 'fixed',
              display: 'block',
              width: formControlData.width,
              top: isOverSize ? formControlData?.top! - popData.clientHeight - modalData?.top! : formControlData?.top! + formControlData?.height! - modalData?.top!,
              left: formControlData?.left! - this.sugsRef.current.closest(`.${ns}Modal-body`)?.getBoundingClientRect().left!
            }
          }
        } else {
          outerStyle = {
            position: 'fixed',
            display: 'block',
            width: formControlData.width,
            top: isOverSize ? formControlData?.top! - popData.clientHeight : formControlData?.top! + formControlData?.height!,
            left: formControlData?.left!,
          }
        }
        this.setState({ outerStyle })
      }
    }
  }

  componentWillUnmount() {
    // 用完销毁
    if (this.intersection.observer) {
      this.intersection.observer?.disconnect?.()
      this.intersection = {}
    }
    if (this.popover) {
      const { classPrefix: ns, } = this.props
      const tplPopTop = this.popover.querySelector(`.${ns}Footer-tplPop-top`) as HTMLElement;
      const tplPopRight = this.popover.querySelector(`.${ns}Footer-tplPop-right`) as HTMLElement;
      const tplPopLeft = this.popover.querySelector(`.${ns}Footer-tplPop-left`) as HTMLElement;
      const tplPopBottom = this.popover.querySelector(`.${ns}Footer-tplPop-bottom`) as HTMLElement;
      tplPopTop.removeEventListener('mousemove', this.onMousedown);
      tplPopRight.removeEventListener('mousemove', this.onMousedown);
      tplPopLeft.removeEventListener('mousemove', this.onMousedown);
      tplPopBottom.removeEventListener('mousemove', this.onMousedown);
    }
    if (this.foucsTimer) clearInterval(this.foucsTimer)
    this.unHook && this.unHook();
    this.input = null
    clearTimeout(this.timer);
  }

  inputRef(ref: any) {
    this.input = ref;
    if (ref && ref.closest?.('td')) {
      this.setState({ inputWidth: getComputedStyle(this.input?.closest?.('td') as Element)?.width })

    }
  }

  focus() {
    if (!this.input) {
      return;
    }
    const { inputfocus, handleInputFocus } = this.props
    this.input.focus();

    // 光标放到最后
    const len = this.input.value.length;
    len && this.input.setSelectionRange(len, len);
    inputfocus && handleInputFocus && handleInputFocus()
  }

  clearValue() {
    const { onChange, resetValue } = this.props;

    onChange(resetValue);
    this.setState(
      {
        inputValue: resetValue
      },
      () => {
        this.focus();
        this.loadAutoComplete();
      }
    );
  }

  removeItem(index: number) {
    const {
      selectedOptions,
      onChange,
      joinValues,
      extractValue,
      delimiter,
      valueField
    } = this.props;

    const newValue = selectedOptions.concat();
    newValue.splice(index, 1);

    onChange(
      joinValues
        ? newValue
          .map(item => item[valueField || 'value'])
          .join(delimiter || ',')
        : extractValue
          ? newValue.map(item => item[valueField || 'value'])
          : newValue
    );
  }

  handleClick() {
    // this.focus();
    this.input?.focus()
    this.addAutoFocus()
    this.setState({
      isOpen: true
    });
  }

  handleFocus(e: any) {
    this.setState({
      isOpen: true,
      isFocused: true,
    });
    this.addAutoFocus()
    this.props.onFocus && this.props.onFocus(e);
  }

  handleBlur(e: any) {
    const { onBlur, trimContents, value, onChange } = this.props;

    this.setState(
      {
        isFocused: false
      },
      () => {
        if (trimContents && value && typeof value === 'string') {
          onChange?.(value.trim());
        }
      }
    );

    onBlur && onBlur(e);
  }

  handleInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
    let value = evt.currentTarget.value;
    const { creatable, multiple, onChange } = this.props;

    this.setState(
      {
        inputValue: value
      },
      () => {
        if (creatable !== false && !multiple) {
          onChange?.(value);
        }

        this.loadAutoComplete();
      }
    );
  }

  handleKeyDown(evt: React.KeyboardEvent<HTMLInputElement>) {
    const {
      selectedOptions,
      onChange,
      joinValues,
      extractValue,
      delimiter,
      multiple,
      valueField,
      creatable
    } = this.props;

    if (selectedOptions.length && !this.state.inputValue && evt.keyCode === 8) {
      evt.preventDefault();
      const newValue = selectedOptions.concat();
      newValue.pop();

      onChange?.(
        joinValues
          ? newValue
            .map(item => item[valueField || 'value'])
            .join(delimiter || ',')
          : extractValue
            ? newValue.map(item => item[valueField || 'value'])
            : newValue
      );
      this.setState(
        {
          inputValue: ''
        },
        this.loadAutoComplete
      );
    } else if (
      evt.keyCode === 13 &&
      this.state.inputValue &&
      typeof this.highlightedIndex !== 'number'
    ) {
      evt.preventDefault();
      const value = this.state.inputValue;

      if (multiple) {
        if (value && !find(selectedOptions, item => item.value == value)) {
          evt.stopPropagation();
          const newValue = selectedOptions.concat();
          newValue.push({
            label: value,
            value: value
          });

          onChange(
            joinValues
              ? newValue
                .map(item => item[valueField || 'value'])
                .join(delimiter || ',')
              : extractValue
                ? newValue.map(item => item[valueField || 'value'])
                : newValue
          );
        }
      } else {
        onChange(value);
      }

      if (creatable === false || multiple) {
        this.setState(
          {
            inputValue: '',
            isOpen: false
          },
          this.loadAutoComplete
        );
      }
    } else if (
      evt.keyCode === 13 &&
      this.state.isOpen &&
      typeof this.highlightedIndex !== 'number'
    ) {
      this.setState({
        isOpen: false
      });
    }
  }

  handleChange(value: any) {
    const {
      onChange,
      multiple,
      joinValues,
      extractValue,
      delimiter,
      selectedOptions,
      valueField,
      creatable
    } = this.props;

    if (multiple) {
      const newValue = selectedOptions.concat();
      newValue.push({
        label: value,
        value: value
      });

      onChange(
        joinValues
          ? newValue
            .map(item => item[valueField || 'value'])
            .join(delimiter || ',')
          : extractValue
            ? newValue.map(item => item[valueField || 'value'])
            : newValue
      );
    } else {
      onChange(value);
    }

    if (multiple || creatable === false) {
      this.setState(
        {
          inputValue: ''
        },
        this.loadAutoComplete
      );
    }
  }

  handleStateChange(changes: StateChangeOptions<any>) {
    const creatable = this.props.creatable;
    const multiple = this.props.multiple || this.props.multi;
    switch (changes.type) {
      case Downshift.stateChangeTypes.itemMouseEnter:
        this.setState({
          isOpen: true
        });
        break;
      case Downshift.stateChangeTypes.changeInput:
        this.setState({
          isOpen: true
        });
        break;
      default:
        const state: TextState = {};
        if (typeof changes.isOpen !== 'undefined') {
          state.isOpen = changes.isOpen;
        }

        if (typeof changes.highlightedIndex !== 'undefined') {
          this.highlightedIndex = changes.highlightedIndex;
        }

        // 输入框清空
        if (
          !multiple &&
          creatable === false &&
          this.state.isOpen &&
          changes.isOpen === false
        ) {
          state.inputValue = '';
        }

        this.setState(state);
        break;
    }
  }

  handleNormalInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    e.stopPropagation()
    const inputRect = this.input?.getBoundingClientRect() // input的计算大小

    // 如果input被其他元素遮住 阻止输入 返回 失焦
    if (document.elementFromPoint((inputRect?.x || 0) + (inputRect?.width || 0) / 2, (inputRect?.y || 0) + (inputRect?.height || 0) / 2) !== this.input)
      return this.input?.blur()

    const { onChange, updateImmediately } = this.props;

    let value = e.currentTarget.value;

    // Jay
    this.setState({
      inputValue: value,
      defaultValue: value,
    })
    this.props?.onHandleChange?.(this.transformValue(value))
    onChange?.(this.transformValue(value), undefined, updateImmediately);
  }


  // 回车按下触发事件
  handleKeyEnter() {

    const Donm = document.getElementsByTagName("input");
    if (this.props.disabled && Donm.length > 1) {
      for (let index = 0; index < Donm.length; index++) {
        if (Donm[index].type === "text") {
          if (this.props.name === Donm[index].name) {
            Donm[index + 1]?.focus()
            break
          }
        }
      }
    } else {
      this.focus()
    }
  }
  handleKeyArrow(e: any) {
    const { rowIndex, colIndex } = this.props
    let inputarray: number[] = []
    const containerTable = this.input?.closest?.('table')
    const tableId = containerTable?.getAttribute('table-id')
    if (!this.props.quickEditForm) {
      //  触发自己
      EventSub.emit(`${tableId}/${EventEnum.ShowVistiMap}`, rowIndex, colIndex, true)
      // 并稍后聚焦
      focusInputAndChooseInput(e.currentTarget)
    }
    const Donm: any = containerTable?.querySelectorAll(`input[col-index="${this.props.colIndex}"]`)
    for (let index = 0; index < Donm.length; index++) {
      if (Donm[index].type === "text" && this.props.name === Donm[index].name) {
        inputarray.push(index)
      }
    }

    if (inputarray.length) {
      for (let index = 0; index < Donm.length; index++) {
        if (Donm[index].type === "text" && this.props.name === Donm[index].name && e.currentTarget === Donm[index]) {
          inputarray.forEach((item, i) => {
            if (item == index) {
              if (e.key === "ArrowDown") {
                EventSub.emit(`${tableId}/${EventEnum.ShowVistiMap}`, rowIndex + 1, colIndex, true) // 广播订阅事件
                if (i + 1 >= inputarray.length) return
                focusInputAndChooseInput(Donm[inputarray[i + 1]])
              } else {
                EventSub.emit(`${tableId}/${EventEnum.ShowVistiMap}`, rowIndex - 1, colIndex, true) // 广播订阅事件
                if (i - 1 < 0) return
                focusInputAndChooseInput(Donm[inputarray[i - 1]])
              }
            }
          })
          break
        }
      }
    }

  }
  transformValue(value: string) {
    const { transform, isNumber } = this.props;

    if (isNumber) {
      const val = value.trim()
      if (/^[0-9]+$/.test(val)) {
        return parseInt(val)
      }
    }

    if (!transform) {
      return value;
    }

    Object.keys(transform).forEach((key: 'lowerCase' | 'upperCase') => {
      const propValue = transform[key];
      switch (key) {
        case 'lowerCase':
          propValue && (value = value.toLowerCase());
          break;
        case 'upperCase':
          propValue && (value = value.toUpperCase());
          break;
      }
    });

    return value;
  }

  loadAutoComplete() {
    const { formItem, autoComplete, data, multiple, selectedOptions } =
      this.props;

    if (isEffectiveApi(autoComplete, data) && formItem) {
      formItem.loadOptions(
        autoComplete,
        createObject(data, {
          term: this.state.inputValue || '' // (multiple ? '' : selectedOptions[selectedOptions.length - 1]?.value)
        }),
        {
          extendsOptions: true
        }
      );
    }
  }

  reload() {
    const reload = this.props.reloadOptions;
    reload && reload();
  }

  valueToString(value: any) {
    return typeof value === 'undefined' || value === null
      ? ''
      : typeof value === 'string'
        ? value
        : JSON.stringify(value);
  }

  renderSugestMode() {
    const {
      className,
      inputOnly,
      value,
      placeholder,
      classnames: cx,
      disabled,
      name,
      loading,
      clearable,
      options,
      selectedOptions,
      autoComplete,
      labelField,
      valueField,
      multiple,
      creatable,
      borderMode,
      showCount,
      translate: __,
      classPrefix: ns
    } = this.props;
    let type = this.props.type?.replace(/^(?:native|input)\-/, '');
    let count = showCount && selectedOptions?.length > showCount
    return (
      <Downshift
        isOpen={this.state.isOpen && !disabled}
        inputValue={this.state.inputValue}
        onChange={this.handleChange}
        onStateChange={this.handleStateChange}
        selectedItem={selectedOptions?.map(item => item[valueField || 'value'])}
      >
        {({
          getInputProps,
          getItemProps,
          isOpen,
          inputValue,
          selectedItem,
          highlightedIndex
        }) => {
          let filtedOptions =
            inputValue && isOpen && !autoComplete
              ? matchSorter(options, inputValue, {
                keys: [labelField || 'label', valueField || 'value']
              })
              : options;
          const indices = isOpen
            ? mapItemIndex(filtedOptions, selectedItem)
            : {};
          filtedOptions = filtedOptions?.filter(
            (option: any) => !~selectedItem.indexOf(option.value)
          );

          return (
            <div
              className={cx(
                `TextControl-input TextControl-input--withAC`,
                inputOnly ? className : '',
                {
                  'is-opened': isOpen,
                  'TextControl-input--multiple': multiple,
                  [`TextControl-input--border${ucFirst(borderMode)}`]:
                    borderMode
                }
              )}
              onClick={this.handleClick}
            >
              <>
                {placeholder &&
                  !selectedOptions.length &&
                  !this.state.inputValue &&
                  !this.state.isFocused ? (
                  <div className={cx('TextControl-placeholder')}>
                    {placeholder}
                  </div>
                ) : null}

                {
                  (count ? selectedOptions.slice(0, showCount as number + 1) :
                    selectedOptions).map((item, index) => multiple ?
                      <div className={cx('TextControl-value')} key={index}>
                        {
                          index == showCount as number ?
                            <span className={cx('TextControl-valueLabel')}>
                              + {(selectedOptions.length - (showCount as number))}
                            </span>
                            :
                            <>
                              <span
                                className={cx('TextControl-valueIcon')}
                                onClick={this.removeItem.bind(this, index)}
                              >
                                ×
                              </span>
                              <span className={cx('TextControl-valueLabel')}>
                                {`${item[labelField || 'label']}`}
                              </span>
                            </>
                        }

                      </div>
                      : (inputValue && isOpen) || creatable !== false ? null : (
                        <div className={cx('TextControl-value')} key={index}>
                          {item.label}
                        </div>
                      )
                    )
                }
                <Input
                  {...getInputProps({
                    name,
                    ref: this.inputRef,
                    disabled,
                    type,
                    onFocus: this.handleFocus,
                    onBlur: this.handleBlur,
                    onChange: this.handleInputChange,
                    onKeyDown: this.handleKeyDown
                  })}
                  autoComplete="off"
                  size={10}
                  style={{ width: `calc(100% - ${(clearable && !disabled && value) ? '16px' : '0'})` }}
                />
              </>

              {clearable && !disabled && value ? (
                <a
                  onClick={this.clearValue}
                  className={cx('TextControl-clear')}
                >
                  <Icon icon="close" className="icon" />
                </a>
              ) : null}

              {loading ? (
                <Spinner
                  show
                  icon="reload"
                  spinnerClassName={cx('TextControl-spinner')}
                />
              ) : null}

              {isOpen && filtedOptions.length ? (
                <div className={cx('TextControl-sugs')} style={this.state.outerStyle}>
                  {filtedOptions.map((option: any) => {
                    return (
                      <div
                        {...getItemProps({
                          item: option.value,
                          disabled: option.disabled,
                          className: cx(`TextControl-sugItem`, {
                            'is-highlight':
                              highlightedIndex === indices[option.value],
                            'is-disabled': option.disabled
                          })
                        })}
                        key={option.value}
                      >
                        <span>
                          {option.disabled
                            ? option.label
                            : highlight(option.label, inputValue as string)}
                          {option.tip}
                        </span>
                      </div>
                    );
                  })}
                </div>
              ) : null}
            </div>
          );
        }}
      </Downshift>
    );
  }

  renderNormal(): JSX.Element {
    const {
      classPrefix: ns,
      classnames: cx,
      className,
      inputOnly,
      value,
      placeholder,
      onChange,
      disabled,
      readOnly,
      max,
      min,
      step,
      clearable,
      name,
      borderMode,
      prefix,
      suffix,
      data,
      showCounter,
      maxLength,
      domicile,
      popOverContainer,
      defaultOpen,
    } = this.props;
    const type = this.props.type?.replace(/^(?:native|input)\-/, '');

    //获取字符串的字节数
    function getBytesLength(str: string): number {
      return str.replace(/[^\u0000-\u00ff]/g, 'aa').length;
    }

    const { defaultValue, popoverVisibleDefault } = this.state
    const primaryField = (this.props.store as any)?.primaryField
    const primaryVlaue = domicile ? domicile?.data?.[primaryField] ? domicile?.data?.[primaryField] : '' : ''
    const primarylable = domicile ? domicile.label : ''

    const placement = !isMobile() ? 'rightTop' : 'bottomRight';
    const popDefaultOpen = (
      <Popover
        visible={popoverVisibleDefault}
        trigger={'click'}
        overlayClassName={cx('TplPop', 'TplIndex')}
        getPopupContainer={popOverContainer}
        onVisibleChange={(visible) => { this.setState({ popoverVisibleDefault: visible }) }} autoAdjustOverflow
        destroyTooltipOnHide
        placement={placement}
        content={(
          <div ref={this.popoverRef}>
            <div className={cx('TplPopover')}>
              <AntdInput.TextArea
                style={{ width: '100%', height: '100%', color: 'rgba(0,0,0,0.6)' }}
                rows={6}
                placeholder={this.props.translate('CRUD.fillIn')}
                value={defaultValue}
                onChange={(e) => { this.setState({ defaultValue: e.target.value }) }}
                onKeyDown={e => e.stopPropagation()}
                bordered={false}
              />
            </div>
            <div className={cx('Footer-tplPop')}>
              {/* 关闭 */}
              <Button size="small" style={{ marginRight: 10, borderRadius: 5 }} onClick={() => { this.setState({ popoverVisibleDefault: false }) }} >
                {this.props.translate('Dialog.close')}
              </Button>
              {/* 确认 */}
              <Button size="small" type="primary" style={{ borderRadius: 5 }} onClick={() => {
                this.handleDefaultValue()
                this.setState({ popoverVisibleDefault: false })
              }} >
                {this.props.translate('confirm')}
              </Button>
            </div>
            <div className={cx('Footer-tplPop-top')}></div>
            <div className={cx('Footer-tplPop-bottom')}></div>
            <div className={cx('Footer-tplPop-left')}></div>
            <div className={cx('Footer-tplPop-right')}></div>
          </div>
        )}
        title={(
          <div className={cx('Title-tplPop')} >
            <div>{primarylable + ":" + (isNil(primaryVlaue) ? '' : primaryVlaue)}</div>
          </div>
        )}>
        {/* <Icon icon="provincial" className={[cx('TplSaving'), 'icon']} style={{ width: 16, height: 16, fill: '#bbb' }} onClick={(e: Event) => { e.stopPropagation() }} /> */}
        <EditOutlined style={{ fontSize: '0.875rem' }} />
      </Popover>
    )

    return (
      <div
        className={cx(
          'TextControl-input',
          {
            [`TextControl-input--border${ucFirst(borderMode)}`]: borderMode
          },
          inputOnly ? className : ''
        )}
        style={{ position: 'relative', overflow: 'hidden' }}
      >
        {prefix ? (
          <span className={cx('TextControl-inputPrefix')}>
            {filter(prefix, data)}
          </span>
        ) : null}
        {this.state.privateType === 'password' ? <div className={cx('TextControl-simulateInput')} >{this.state.inputValue}
          <div style={{ height: !this.state.inputValue?.slice(0, this.input?.selectionStart || 0) && !this.state.inputValue ? '' : '100%' }} className={cx('TextControl-simulateInput-container')} >{this.state.inputValue?.slice(0, this.input?.selectionStart || 0)}</div>
        </div> : null}
        <input
          name={name}
          col-index={this.props.colIndex}
          onSelect={() => {
            // 这个模式才有这个功能
            if (this.props.keepcursor)
              this.setState({
                privateType: 'password',
                simulateSelectionPosition: this.input?.selectionStart
              })
          }}
          placeholder={placeholder}
          ref={this.inputRef}
          disabled={disabled}
          readOnly={readOnly}
          // 特殊情况的时候要用state的type覆盖传入的type 目前是text有时候不允许输入中文
          type={(this.props.keepcursor && this.state.privateType) || type}
          onFocus={this.handleFocus}
          onBlur={this.handleBlur}
          max={max}
          min={min}
          // Jay
          maxLength={maxLength && (maxLength - getChineseWordLengthInWord(value))}
          autoComplete={this.props.keepcursor ? "false" : 'off'}
          size={10}
          step={step}
          style={{
            textAlign: 'inherit',
            width: `calc(${this.state.inputWidth || '100%'} - ${(clearable && !disabled && value) ? '48px' : '32px'})`,
            userSelect: this.state.privateType === 'password' ? 'none' : 'unset', // 显示密码的时候不展示文本,
            color: this.state.privateType === 'password' ? 'transparent' : '', // 显示密码的时候不展示文本
          }}
          onChange={this.handleNormalInputChange}
          onKeyDown={(e) => {
            if (e.key === "Enter" && this.props.keepcursor) {
              this.timer = setTimeout(() => this.handleKeyEnter(), 200)
            }
            if (this.props.manual && (e.key === "ArrowUp" || e.key === "ArrowDown")) {
              this.handleKeyArrow(e)
            }
            this.setState({
              simulateSelectionPosition: this.input?.selectionStart
            })
          }
          }
          // value={this.valueToString(value)} // Jay 数据太多时会卡顿
          value={this.valueToString(this.state.inputValue)}
        />
        {clearable && !disabled && value ? (
          <a onClick={this.clearValue} className={`${ns}TextControl-clear`}>
            <Icon icon="close" className="icon" />
          </a>
        ) : null}

        {defaultOpen &&
          <a className={`${ns}TextControl-clear ${ns}TextControl-color`}>
            {popDefaultOpen}
          </a>
        }
        {showCounter ? (
          <span className={cx('TextControl-counter')}>
            {`${getBytesLength(typeof value === 'undefined' || value === null
              ? ''
              : typeof value === 'string'
                ? value
                : JSON.stringify(value)
            )}${typeof maxLength === 'number' && maxLength ? `/${maxLength}` : ''
              }`}
          </span >
        ) : null
        }
        {
          suffix ? (
            <span className={cx('TextControl-inputSuffix')}>
              {filter(suffix, data)}
            </span>
          ) : null
        }
      </div >
    );
  }

  haneldText(val: string) {
    const { onChange } = this.props
    this.setState({ inputValue: val || '', popoverVisible: false })
    onChange(val ? this.transformValue(val) : '');
  }
  handleDefaultValue() {
    const { onChange } = this.props;
    const { defaultValue } = this.state;
    if (!defaultValue) {
      this.setState({ popoverVisible: false })
    }
    onChange(defaultValue ? this.transformValue(defaultValue) : '');
  }
  popoverRef = (dom: any) => {
    const { classPrefix: ns, name } = this.props
    this.popover = dom
    if (this.popover) {
      if (localStorage.getItem('textControlInput') && typeof localStorage.getItem('textControlInput') == 'string') {

        let textControlInput = JSON.parse(localStorage.getItem('textControlInput')!)
        if (Array.isArray(textControlInput) && textControlInput.some((item: any) => item.name == name)) {
          const style = textControlInput.find((item: any) => item.name == name).style
          const tplText = this.popover.querySelector(`.${ns}TplPopover`) as HTMLElement;
          tplText.style.height = style.height + 'px';
          tplText.style.width = style.width + 'px';
        }
      }
      const tplPopTop = this.popover.querySelector(`.${ns}Footer-tplPop-top`) as HTMLElement;
      const tplPopRight = this.popover.querySelector(`.${ns}Footer-tplPop-right`) as HTMLElement;
      const tplPopLeft = this.popover.querySelector(`.${ns}Footer-tplPop-left`) as HTMLElement;
      const tplPopBottom = this.popover.querySelector(`.${ns}Footer-tplPop-bottom`) as HTMLElement;

      tplPopTop.addEventListener('mousedown', this.onMousedown);
      tplPopRight.addEventListener('mousedown', this.onMousedown);
      tplPopLeft.addEventListener('mousedown', this.onMousedown);
      tplPopBottom.addEventListener('mousedown', this.onMousedown);
    }
  }
  onMousedown = (e: any) => {
    e.preventDefault();
    e.stopPropagation();
    const { classPrefix: ns, name } = this.props

    if (!this.popover || !document.defaultView) return
    const tplText = this.popover.querySelector(`.${ns}TplPopover`) as HTMLElement;
    const upErDown = e.target.className.includes('top') || e.target.className.includes('bottom')
    const about = e.target.className.includes('left') || e.target.className.includes('right')
    let textControlInput: any[] = []
    if (localStorage.getItem('textControlInput') && typeof localStorage.getItem('textControlInput') == 'string') {

      textControlInput = JSON.parse(localStorage.getItem('textControlInput')!)
      if (textControlInput instanceof Object) {
        textControlInput = []
      }
    }

    if (e.target.tagName.toLowerCase() === 'div') {
      // 记录初始鼠标位置和弹窗初始宽高
      let startX = e.clientX;
      let startY = e.clientY;

      let startWidth = parseInt(document?.defaultView.getComputedStyle(tplText).width, 10);
      let startHeight = parseInt(document?.defaultView.getComputedStyle(tplText).height, 10);
      // 设置鼠标移动事件
      document.addEventListener('mousemove', resizeElement);
      // 设置鼠标松开事件
      document.addEventListener('mouseup', function () {
        document.removeEventListener('mousemove', resizeElement);
      });

      // 调整元素大小函数
      function resizeElement(e: any) {
        let newWidth = startWidth + e.clientX - startX;
        let newHeight = startHeight + e.clientY - startY;
        if (newWidth < 180) return
        if (newHeight < 80) return
        if (upErDown) {
          tplText.style.height = newHeight + 'px';
        }
        if (about) {
          tplText.style.width = newWidth + 'px';
        }
        if (textControlInput.some(item => item.name == name)) {
          const content = {
            name: name,
            style: {
              width: newWidth,
              height: newHeight,
            }
          }
          textControlInput = [...textControlInput.filter(item => item.name !== name), content]
        } else {
          const content = {
            name: name,
            style: {
              width: newWidth,
              height: newHeight,
            }
          }
          textControlInput = [...textControlInput, content]

        }

        localStorage.setItem('textControlInput', JSON.stringify(textControlInput))
      }
    }
  }

  render(): JSX.Element {
    const {
      classnames: cx,
      className,
      classPrefix: ns,
      options,
      source,
      autoComplete,
      addOn: addOnRaw,
      render,
      data,
      disabled,
      inputOnly,
      onChange,
      translate: __,
      env
    } = this.props;

    const { popoverVisible, multipleValues } = this.state

    const addOn: any =
      typeof addOnRaw === 'string'
        ? {
          label: addOnRaw,
          type: 'plain'
        }
        : addOnRaw;

    let input =
      autoComplete !== false && (source || options?.length || autoComplete)
        ? this.renderSugestMode()
        : this.renderNormal();

    const iconElement = generateIcon(cx, addOn?.icon, 'Icon');

    let addOnDom = addOn ? (
      addOn.actionType ||
        ~['button', 'submit', 'reset', 'action'].indexOf(addOn.type) ? (
        <div className={cx(`${ns}TextControl-button`, addOn.className)}>
          {render('addOn', addOn, {
            disabled
          })}
        </div>
      ) : (
        <div className={cx(`${ns}TextControl-addOn`, addOn.className)}>
          {addOn.label ? filter(addOn.label, data) : null}
          {iconElement}
        </div>
      )
    ) : null;

    const popoverDom = <TextareaPop visible={popoverVisible} translate={__} values={multipleValues}
      onVisibleChange={(visible: boolean) => this.setState({ popoverVisible: visible })}
      onValuesChange={(values: string) => this.setState({ multipleValues: values })}
      onReset={() => {
        this.setState({ inputValue: '', multipleValues: '' });
        onChange('');
      }}
      classnames={cx} classPrefix={ns} handleText={this.haneldText} icon={this.props.icon}
    />;

    if (inputOnly) {
      // return input;
      // Jay 支持多项值填入
      return <div className={cx(className, {
        // 'is-focused': this.state.isFocused,
        'is-disabled': disabled,
        [`${ns}TextControl--withPopover`]: this.props.isMultipleValues,
        'is-showCount': this.props.showCount
      })}>
        {input}
        {this.props.isMultipleValues && popoverDom}
      </div>;
    }

    return (
      <div
        className={cx(className, `${ns}TextControl`, {
          [`${ns}TextControl--withAddOn`]: !!addOnDom,
          // 'is-focused': this.state.isFocused,
          'is-disabled': disabled,
          [`${ns}TextControl--withPopover`]: this.props.isMultipleValues,
          'is-showCount': this.props.showCount,
        })}
        ref={this.sugsRef}
      >
        {addOn && addOn.position === 'left' ? addOnDom : null}
        {input}
        {addOn && addOn.position !== 'left' ? addOnDom : null}
        {/* Jay 支持多项值填入 */}
        {this.props.isMultipleValues && popoverDom}
      </div>
    );
  }
}

export function mapItemIndex(
  items: Array<any>,
  values: Array<any>,
  valueField: string = 'value'
) {
  return items
    ?.filter(item => values.indexOf(item[valueField || 'value']) === -1)
    .reduce((prev, next, i) => {
      prev[next[valueField || 'value']] = i;
      return prev;
    }, {});
}

@OptionsControl({
  type: 'input-text'
})
export class TextControlRenderer extends TextControl { }

@OptionsControl({
  type: 'input-password'
})
export class PasswordControlRenderer extends TextControl { }

@OptionsControl({
  type: 'input-email',
  validations: 'isEmail'
})
export class EmailControlRenderer extends TextControl { }

@OptionsControl({
  type: 'input-url',
  validations: 'isUrl'
})
export class UrlControlRenderer extends TextControl { }

@OptionsControl({
  type: 'native-date'
})
export class NativeDateControlRenderer extends TextControl { }

@OptionsControl({
  type: 'native-time'
})
export class NativeTimeControlRenderer extends TextControl { }

@OptionsControl({
  type: 'native-number'
})
export class NativeNumberControlRenderer extends TextControl { }
