/**
 * 后端已经不再使用input-table，全部使用input-table-field
 */
import React from 'react';
import { FormItem, FormControlProps, FormBaseControl } from './Item';
import Button from '../../components/Button';
import {
  createObject,
  getTree,
  getVariable,
  setVariable,
  spliceTree
} from '../../utils/helper';
import { RendererData, Action, Api, Payload, ApiObject } from '../../types';
import { isEffectiveApi } from '../../utils/api';
import { filter } from '../../utils/tpl';
import omit from 'lodash/omit';
import { dataMapping } from '../../utils/tpl-builtin';
import findIndex from 'lodash/findIndex';
import { SimpleMap } from '../../utils/SimpleMap';
import { Icon } from '../../components/icons';
import { TableSchema } from '../Table';
import { SchemaApi } from '../../Schema';
import find from 'lodash/find';
// import Scoped, { ScopedContext, IScopedContext } from '../../Scoped';

// Jay
import { IScopedContext } from '../../Scoped';
import { ActionSchema } from '../Action';
import { Schema, SchemaNode } from '../../types';
import { CRUDStore, ICRUDStore } from '../../store/crud';
import {
  uniqueArr,
} from '../../utils/helper';
import Popconfirm from 'antd/lib/Popconfirm'
import cloneDeep from 'lodash/cloneDeep';
import { List } from 'immutable';
import debounce from 'lodash/debounce';

export interface TableControlSchema
  extends FormBaseControl,
  Omit<TableSchema, 'type'> {
  type: 'input-table';

  /**
   * 可新增
   */
  addable?: boolean;

  /**
   * 可复制新增
   */
  copyable?: boolean;

  /**
   * 复制按钮文字
   */
  copyBtnLabel?: string;

  /**
   * 复制按钮图标
   */
  copyBtnIcon?: string;

  /**
   * 是否显示复制按钮
   */
  copyAddBtn?: boolean;

  /**
   * 是否可以拖拽排序
   */
  draggable?: boolean;

  /**
   * 新增 API
   */
  addApi?: SchemaApi;

  /**
   * 新增按钮文字
   */
  addBtnLabel?: string;

  /**
   * 新增按钮图标
   */
  addBtnIcon?: string;

  /**
   * 显示新增按钮
   */
  showAddBtn?: boolean;

  /**
   * 可否删除
   */
  removable?: boolean;

  /**
   * 删除的 API
   */
  deleteApi?: SchemaApi;

  /**
   * 可否编辑
   */
  editable?: boolean;

  /**
   * 更新按钮名称
   */
  editBtnLabel?: string;

  /**
   * 更新按钮图标
   */
  editBtnIcon?: string;

  /**
   * 确认按钮文字
   */
  confirmBtnLabel?: string;

  /**
   * 确认按钮图标
   */
  confirmBtnIcon?: string;

  /**
   * 取消按钮文字
   */
  cancelBtnLabel?: string;

  /**
   * 取消按钮图标
   */
  cancelBtnIcon?: string;

  /**
   * 删除按钮文字
   */
  deleteBtnLabel?: string;

  /**
   * 删除按钮图标
   */
  deleteBtnIcon?: string;

  /**
   * 更新 API
   */
  updateApi?: SchemaApi;

  /**
   * 初始值，新增的时候
   */
  scaffold?: any;

  /**
   * 删除确认文字
   */
  deleteConfirmText?: string;

  /**
   * 值字段
   */
  valueField?: string;

  /**
   * 是否为确认的编辑模式。
   */
  needConfirm?: boolean;

  /**
   * 是否可以访问父级数据，正常 combo 已经关联到数组成员，是不能访问父级数据的。
   */
  canAccessSuperData?: boolean;

  /**
   * 是否显示序号
   */
  showIndex?: boolean;

  /**
   * 分页个数，默认不分页
   */
  perPage?: number;

  // Jay
  /**
   * 批量操作
   */
  bulkActions?: Array<ActionSchema>;

  /**
   * 单条操作
   */
  itemActions?: Array<ActionSchema>;
  tableLayout: 'vertical' | 'horizontal';
  updateType?: string
}

export interface TableProps
  extends FormControlProps,
  Omit<
    TableControlSchema,
    'type' | 'className' | 'descriptionClassName' | 'inputClassName'
  > {
  // Jay
  store: ICRUDStore;
  onInputTableChange: (value: any, name: string) => void;
  name: string;
}

export interface TableState {
  items: Array<any>;
  raw?: any;
  columns: Array<any>;
  editIndex: number;
  isCreateMode?: boolean;
  page?: number;
  // Jay
  operationType: 'add' | 'edit' | 'delete' | undefined; // 增删改
  insertItems: Array<any>;
  updateItems: Array<any>;
  deleteItems: Array<any>;
  hasClickedToolbar?: boolean; // 是否点击过工具栏
  tableRotate: boolean; //移动端表格横屏模式
  [key: string]: any;

}

export default class FormTable extends React.Component<TableProps, TableState> {
  static defaultProps = {
    placeholder: '空',
    scaffold: {},
    addBtnIcon: 'plus',
    copyBtnIcon: 'copy',
    editBtnIcon: 'pencil',
    deleteBtnIcon: 'minus',
    confirmBtnIcon: 'check',
    cancelBtnIcon: 'close',
    valueField: '',
    tableLayout: 'vertical'
  };

  static propsList: Array<string> = [
    'onChange',
    'name',
    'columns',
    'label',
    'scaffold',
    'showAddBtn',
    'addable',
    'removable',
    'copyable',
    'editable',
    'addApi',
    'updateApi',
    'deleteApi',
    'needConfirm',
    'canAccessSuperData',
    'formStore',
    'updateType'
  ];

  entries: SimpleMap<any, number>;
  entityId: number = 1;
  subForms: any = {};
  rowPrinstine: Array<any> = [];
  editting: any = {};
  constructor(props: TableProps) {
    super(props);
    // Jay
    // 为原有的行数据添加isOrigin标识，代表是原有的
    const items = Array.isArray(props.value) ? cloneDeep(props.value) : []
    // 这是针对于非异步请求的数据
    items.forEach((item: any) => item.isOrigin = true)

    this.state = {
      columns: this.buildColumns(props),
      editIndex: -1,
      // items: Array.isArray(props.value) ? props.value.concat() : [],
      items,
      operationType: undefined,
      insertItems: [],
      updateItems: [],
      deleteItems: [],
      hasClickedToolbar: false,
      tableRotate: false //移动端表格横屏模式
    };

    this.entries = new SimpleMap();
    this.buildItemProps = this.buildItemProps.bind(this);
    this.confirmEdit = this.confirmEdit.bind(this);
    this.cancelEdit = this.cancelEdit.bind(this);
    this.handleSaveTableOrder = this.handleSaveTableOrder.bind(this);
    this.handleTableSave = this.handleTableSave.bind(this)
    this.getEntryId = this.getEntryId.bind(this);
    this.subFormRef = this.subFormRef.bind(this);
    this.handlePageChange = this.handlePageChange.bind(this);
    this.emitValue = this.emitValue.bind(this);
    // Jay
    this.startEdit = this.startEdit.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleStoreChange = debounce(this.handleStoreChange.bind(this), 600)
    this.renderHeaderToolbar = this.renderHeaderToolbar.bind(this)
    this.renderFooterToolbar = this.renderFooterToolbar.bind(this)
    this.editing = false
    // Aug
    this.handleTableBatchEdit = this.handleTableBatchEdit.bind(this);
    this.handleAction = this.handleAction.bind(this)
  }

  // Jay
  // DidMount时没有增删改，对form组件的store.inputTableData[name]赋值为空对象
  componentDidMount() {
    this.emitValue()
  }

  componentDidUpdate(prevProps: TableProps, prevState: TableState) {
    const props = this.props;
    let toUpdate: any = null;
    if (props.columns !== prevProps.columns) {
      toUpdate = {
        ...toUpdate,
        columns: this.buildColumns(props)
      };
    }
    if (props.value !== prevProps.value) {
      // Jay
      // 为原有的行数据添加isOrigin标识，代表是原有的
      // ...this.state.items是为了保留之前的数据
      // 因为有这种情况出现，通过form的target传过来的数据会把之前value覆盖掉（例如form.target + picker回显），导致input-table之前的值丢失
      // new Set去重sdk会出错
      // 通过autoFill填充的数据直接覆盖之前的数据
      // 通过顶部工具栏添加的数据也是需要保留之前的数据
      // props.updateType === 'reload' 当数据来源为reload，直接覆盖之前的数据
      const temp: any[] = Array.isArray(props.value) ?
        this.props.autoFillObj[this.props.name] || props.updateType === 'reload' ?
          props.value : [...(!this.editing ? this.state.items : this.items), ...props.value.slice(this.state.items.length)]
        : []
      this.editing && (this.editing = false)
      // 这行是针对于异步请求获取的value
      // !this.state.hasClickedToolbar是指不是通过点击工具栏（picker）等动作获取数据
      // autoFillObj：wrapControl传过来的props，Options.tsx syncAutoFill方法: 只有点击具有autoFill的表单项，才会保存autoFill对象到store.autoFillObj
      const isOrigin = (prevProps.value?.length === 0 || !prevProps.value) && !this.state.hasClickedToolbar && !this.props.autoFillObj[this.props.name]

      const items = List(temp)
      items.forEach((item: any) => {
        if (isOrigin) {
          //这里会报错，因为item的isOrigin属性是只读的，无法赋值
          item.isOrigin = true
        }
      })

      toUpdate = {
        ...toUpdate,
        items: items.toJSON().filter(iobj => !!iobj),
        editIndex: -1,
        raw: undefined
      };
      // Jay 重置store.autoFillObj，若不重置，则this.props.autoFillObj[this.props.name]就有可能一直为true，就无法判断是否是autoFill回传的数据
      if (this.props.autoFillObj[this.props.name]) {
        this.props.store?.setAutoFillObj({})
      }
      // Aug 重置store.updateType
      if (props.updateType === 'reload') {
        props.store?.setUpdateType()
      }
    }
    toUpdate && this.setState(toUpdate, this.handleInputChange);
  }

  componentWillUnmount() {
    this.entries.dispose();
  }

  subFormRef(form: any, x: number, y: number) {
    this.subForms[`${x}-${y}`] = form;
  }

  async validate(): Promise<string | void> {
    const { value, minLength, maxLength, translate: __, columns } = this.props;

    // todo: 如果当前正在编辑中，表单提交了，应该先让正在编辑的东西提交然后再做验证。
    if (~this.state.editIndex) {
      return __('Table.editing');
    }

    if (minLength && (!Array.isArray(value) || value.length < minLength)) {
      return __('Combo.minLength', { minLength });
    } else if (maxLength && Array.isArray(value) && value.length > maxLength) {
      return __('Combo.maxLength', { maxLength });
    } else {
      const subForms: Array<any> = [];
      Object.keys(this.subForms).forEach(
        key => this.subForms[key] && subForms.push(this.subForms[key])
      );
      if (subForms.length) {
        const results = await Promise.all(
          subForms.map(item => item.validate())
        );

        let msg = ~results.indexOf(false) ? __('Form.validateFailed') : '';
        let uniqueColumn = '';

        if (
          !msg &&
          Array.isArray(columns) &&
          Array.isArray(value) &&
          columns.some(item => {
            if (item.unique && item.name) {
              let exists: Array<any> = [];

              return value.some((obj: any) => {
                const value = getVariable(obj, item.name);

                if (~exists.indexOf(value)) {
                  uniqueColumn = `${item.label || item.name}`;
                  return true;
                }

                exists.push(value);
                return false;
              });
            }

            return false;
          })
        ) {
          msg = __('InputTable.uniqueError', {
            label: uniqueColumn
          });
        }

        return msg;
      }
    }
  }

  emitValue() {
    const items = (this.state.items).filter(item => !item.__isPlaceholder);
    const { onChange } = this.props;
    onChange?.(items);
    // Jay
    this.handleInputChange()
  }

  async doAction(action: Action, ctx: RendererData, ...rest: Array<any>) {
    const {
      onAction,
      valueField,
      env,
      onChange,
      editable,
      needConfirm,
      addable,
      addApi,
      translate: __
    } = this.props;

    if (action.actionType === 'add') {
      if (addable === false) {
        return;
      }

      const items = this.state.items.concat();

      if (addApi || action.payload) {
        let toAdd = null;

        if (isEffectiveApi(addApi, ctx)) {
          const payload = await env.fetcher(addApi, ctx);
          if (payload && !payload.ok) {
            env.notify('error', payload.msg || __('fetchFailed'));
            return;
          } else if (payload && payload.ok) {
            toAdd = payload.data;
          }
        } else {
          toAdd = dataMapping(action.payload, ctx);
        }

        toAdd = Array.isArray(toAdd) ? toAdd : [toAdd];
        toAdd.forEach((toAdd: any) => {
          if (
            !valueField ||
            !find(
              items,
              item => item[valueField as string] == toAdd[valueField as string]
            )
          ) {
            // 不要重复加入
            items.push(toAdd);
          }
        });

        this.setState(
          {
            items
          },
          () => {
            this.emitValue();

            if (toAdd.length === 1 && needConfirm !== false) {
              this.startEdit(items.length - 1, true);
            }
          }
        );

        return;
      } else {
        return this.addItem(items.length - 1);
      }
    } else if (
      action.actionType === 'remove' ||
      action.actionType === 'delete'
    ) {
      if (!valueField) {
        return env.alert(__('Table.valueField'));
      } else if (!action.payload) {
        return env.alert(__('Table.playload'));
      }

      const items = this.state.items.concat();
      let toRemove: any = dataMapping(action.payload, ctx);
      toRemove = Array.isArray(toRemove) ? toRemove : [toRemove];

      toRemove.forEach((toRemove: any) => {
        const idx = findIndex(
          items,
          item => item[valueField as string] == toRemove[valueField as string]
        );
        if (~idx) {
          items.splice(idx, 1);
        }
      });

      this.setState(
        {
          items
        },
        () => this.emitValue()
      );

      // todo 如果配置删除 Api 怎么办？
      return;
    }

    return onAction && onAction(action, ctx, ...rest);
  }

  copyItem(index: number) {
    const { needConfirm } = this.props;
    const items = this.editing ? this.items : this.state.items.concat(); // Jay
    // Jay
    let item = items[index]
    if (item.isOrigin) {
      item = cloneDeep(items[index])
      delete item.isOrigin
    }

    // items.splice(index + 1, 0, items[index]);
    items.splice(index + 1, 0, item);

    index = Math.min(index + 1, items.length - 1);
    this.setState(
      {
        items
      },
      () => {
        if (needConfirm === false) {
          this.emitValue();
        } else {
          this.startEdit(index, true);
        }
      }
    );
  }

  // Jay 整理insert、update、delete数据结构
  arrangeData(temp?: any[]) {
    const { deleteItems } = this.state
    const items = temp || this.state.items
    let data: any = {};
    const insertItems = items.filter(item => !item.isOrigin || item.isAdd)
    const updateItems = items.filter(item => item.isOrigin && item.isUpdate)
    if (insertItems.length !== 0) {
      data.insert = cloneDeep(insertItems).map((item: any) => {
        item.isAdd && delete item.isAdd
        item.isUpdate && delete item.isUpdate
        // 对主从表的ids、items、selectedItems做兼容处理，如果还有问题就去处理主从表 LionTable.tsx renderToolBars
        item.items && delete item.items
        item.selectedItems && delete item.selectedItems
        item.ids && delete item.ids
        return item
      })
    }
    if (updateItems.length !== 0) {
      data.update = cloneDeep(updateItems).map((item: any) => {
        delete item.isOrigin
        delete item.isUpdate

        item.items && delete item.items
        item.selectedItems && delete item.selectedItems
        item.ids && delete item.ids
        return item
      })
    }
    if (deleteItems.length !== 0) {
      data.delete = cloneDeep(deleteItems).map((item: any) => {
        delete item.isOrigin
        item.isUpdate && delete item.isUpdate

        item.items && delete item.items
        item.selectedItems && delete item.selectedItems
        item.ids && delete item.ids
        return item
      })
    }
    return data
  }

  addItem(index: number) {
    const { needConfirm, scaffold, columns } = this.props;
    const items = this.editing ? this.items : this.state.items.concat(); // Jay
    let value: any = {
      __isPlaceholder: true
    };

    if (Array.isArray(columns)) {
      columns.forEach(column => {
        if (
          typeof column.value !== 'undefined' &&
          typeof column.name === 'string'
        ) {
          setVariable(value, column.name, column.value);
        }
        // Jay
        // 新建行时每列的值为null
        else if (typeof column.name === 'string') {
          value[column.name] = null
        }
      });
    }
    value = {
      ...value,
      ...scaffold
    };

    if (needConfirm === false) {
      delete value.__isPlaceholder;
    }

    items.splice(index + 1, 0, value);
    index = Math.min(index + 1, items.length - 1);
    this.setState(
      {
        items,
        hasClickedToolbar: true
      },
      () => {
        if (needConfirm === false) {
          // this.emitValue();
          // Aug 修复新增时table数据域没有更新的问题
          this.handleInputChange(items)
          this.handleStoreChange(items)
        } else {
          this.startEdit(index, true);
        }
      }
    );
  }

  startEdit(index: number, isCreate: boolean = false) {
    this.setState({
      editIndex: index,
      isCreateMode: isCreate,
      raw: (this.editing ? this.items : this.state.items)[index],

      columns: this.buildColumns(this.props, isCreate)
    });
  }

  async confirmEdit() {
    const { addApi, updateApi, data, env, translate: __ } = this.props;

    // form 是 lazyChange 的，先让他们 flush, 即把未提交的数据提交。
    const subForms: Array<any> = [];
    Object.keys(this.subForms).forEach(
      key => this.subForms[key] && subForms.push(this.subForms[key])
    );
    subForms.forEach(form => form.flush());

    const items = this.state.items.concat();
    let item = {
      ...items[this.state.editIndex]
    };
    const isNew = item.__isPlaceholder;

    let remote: Payload | null = null;
    // Jay
    // 提交数据的结构改成和运行平台的增删改
    const ctx: any = createObject(data, item)
    delete ctx.__isPlaceholder
    if (isNew && isEffectiveApi(addApi, createObject(data, item))) {
      // remote = await env.fetcher(addApi, createObject(data, item));
      remote = await env.fetcher(addApi, {
        insert: [{ ...ctx }]
      });
    } else if (isEffectiveApi(updateApi, createObject(data, item))) {
      // remote = await env.fetcher(updateApi, createObject(data, item));
      remote = await env.fetcher(updateApi, {
        update: [{ ...ctx }]
      });
    }

    if (remote && !remote.ok) {
      env.notify('error', remote.msg || __('saveFailed'));
      return;
    } else if (remote && remote.ok) {
      item = {
        ...(((isNew ? addApi : updateApi) as ApiObject).replaceData
          ? {}
          : item),
        ...remote.data
      };
    }

    delete item.__isPlaceholder;

    // Jay
    const { operationType } = this.state
    if (operationType === 'add') {
      if (!item.isOrigin) {
        this.setState({
          insertItems: [...this.state.insertItems, item]
        }, this.handleInputChange)
      }
    } else if (operationType === 'edit') {
      // 未提交表单前编辑新增行不计入updateItems
      if (item.isOrigin) {
        item.isUpdate = true
        this.setState({
          updateItems: [...this.state.updateItems, item]
        }, this.handleInputChange)
      } else {
        // 如果编辑的是新增行，则替换掉insertItems中对应的元素
        this.setState({
          insertItems: this.state.items.filter(item => !item.isOrigin)
        }, this.handleInputChange)
      }
    }

    items.splice(this.state.editIndex, 1, item);

    this.setState(
      {
        editIndex: -1,
        items: items,
        raw: undefined
      },
      this.emitValue
    );
  }

  cancelEdit() {
    let items = this.state.items.concat();

    if (this.state.isCreateMode) {
      items = items.filter(item => !item.__isPlaceholder);
    } else if (this.state.raw) {
      items.splice(this.state.editIndex, 1, this.state.raw);
    }

    this.setState(
      {
        editIndex: -1,
        raw: undefined,
        items: items
      },
      this.emitValue
    );
  }

  async removeItem(index: number) {
    const {
      value,
      onChange,
      deleteApi,
      deleteConfirmText,
      env,
      data,
      translate: __
    } = this.props;
    // let newValue = Array.isArray(value) ? value.concat() : [];
    // Jay
    // value是父组件传过来的，如果是通过form的target传过来
    // 为什么使用this.state.items：因为有这种情况出现，通过form的target传过来的数据会把之前value覆盖掉（例如form.target + picker回显），导致input-table之前的值丢失

    let newValue = Array.isArray(this.state.items) ? this.editing ? this.items : this.state.items : []; // Jay
    const item = newValue[index];
    if (!item) {
      return;
    }

    const ctx: any = createObject(data, item);
    if (isEffectiveApi(deleteApi, ctx)) {
      // Jay
      // 因为删除按钮BUtton加了Popconfirm，所以不用再弹窗确认了

      // const confirmed = await env.confirm(
      //   deleteConfirmText ? filter(deleteConfirmText, ctx) : __('deleteConfirm')
      // );
      // if (!confirmed) {
      //   // 如果不确认，则跳过！
      //   return;
      // }

      // 提交数据的结构改成和运行平台的增删改
      // const result = await env.fetcher(deleteApi, ctx);
      ctx.isOrigin && delete ctx.isOrigin
      ctx.isUpdate && delete ctx.isUpdate
      const result = await env.fetcher(deleteApi, {
        delete: [{ ...ctx }]
      });

      if (!result.ok) {
        env.notify('error', __('deleteFailed'));
        return;
      }
    }

    this.removeEntry(item);
    newValue.splice(index, 1);
    // onChange(newValue)
    // Jay
    // 这样是让componentDidUpdate中的this.state.items始终是最新值

    // this.setState({
    //   items: newValue
    // }, () => {
    //   onChange(newValue)
    // })
    // Aug 修复删除时table数据域没有更新的问题
    this.handleInputChange(newValue)
    this.handleStoreChange(newValue)

    // Jay
    if (this.state.operationType === 'delete') {
      // 未提交表单前删除新增行不计入deleteItems
      // 若编辑过原有的行，然后再删除该行则不计入updateItems
      if (item.isOrigin) {
        const updateItems = newValue.filter(item => item.isOrigin && item.isUpdate)

        this.setState({
          updateItems,
          deleteItems: [...this.state.deleteItems, item]
        }
          // Aug 上面已经有了这里就不用了吧
          // , this.handleInputChange
        )
      } else {
        // 未提交表单前删除新增行，要把insertItems的item删除掉
        this.setState({
          insertItems: newValue.filter(item => !item.isOrigin)
        }
          // Aug 上面已经有了这里就不用了吧
          // , this.handleInputChange
        )
      }
    }
  }

  buildItemProps(item: any, index: number) {
    if (this.props.needConfirm === false) {
      return {
        quickEditEnabled: true
      };
    } else if (
      !this.props.editable &&
      !this.props.addable &&
      !this.state.isCreateMode
    ) {
      return null;
    }

    const perPage = this.props.perPage;
    const page = this.state.page || 1;
    let offset = 0;
    if (typeof perPage === 'number' && perPage) {
      offset = (page - 1) * perPage;
    }

    return {
      quickEditEnabled: this.state.editIndex === index + offset
    };
  }

  buildColumns(props: TableProps, isCreateMode = false): Array<any> {
    const env = this.props.env;
    let columns: Array<any> = Array.isArray(props.columns)
      ? props.columns.concat()
      : [];
    const ns = this.props.classPrefix;
    const __ = this.props.translate;
    const needConfirm = this.props.needConfirm;
    const showIndex = this.props.showIndex;

    let btns = [];
    if (props.addable && props.showAddBtn !== false) {
      btns.push({
        children: ({
          key,
          rowIndex,
          offset
        }: {
          key: any;
          rowIndex: number;
          offset: number;
        }) =>
          ~this.state.editIndex && needConfirm !== false ? null : (
            <Button
              classPrefix={ns}
              size="sm"
              key={key}
              level="link"
              tooltip={__('Table.addRow')}
              tooltipContainer={
                env?.getTopModalContainer || undefined
              }
              onClick={() => {
                // Jay
                this.setState({
                  operationType: 'add'
                }, this.addItem.bind(this, rowIndex + offset, undefined))
              }}
            >
              {props.addBtnLabel ? <span>{props.addBtnLabel}</span> : null}
              {props.addBtnIcon ? (
                <Icon icon={props.addBtnIcon} className="icon" />
              ) : null}
            </Button>
          )
      });
    }

    if (props.copyable && props.showCopyBtn !== false) {
      btns.push({
        children: ({
          key,
          rowIndex,
          offset
        }: {
          key: any;
          rowIndex: number;
          offset: number;
        }) =>
          ~this.state.editIndex && needConfirm !== false ? null : (
            <Button
              classPrefix={ns}
              size="sm"
              key={key}
              level="link"
              tooltip={__('Table.copyRow')}
              tooltipContainer={
                env?.getTopModalContainer || undefined
              }
              // onClick={this.copyItem.bind(this, rowIndex + offset, undefined)}
              // Jay
              onClick={() => {
                this.setState({
                  operationType: 'add'
                }, this.copyItem.bind(this, rowIndex + offset, undefined))
              }}
            >
              {props.copyBtnLabel ? <span>{props.copyBtnLabel}</span> : null}
              {props.copyBtnIcon ? (
                <Icon icon={props.copyBtnIcon} className="icon" />
              ) : null}
            </Button>
          )
      });
    }

    if (props.needConfirm === false) {
      columns = columns.map(column => {
        const quickEdit = column.quickEdit;

        return quickEdit === false
          ? omit(column, ['quickEdit'])
          : {
            ...column,
            quickEdit: {
              ...this.columnToQuickEdit(column),
              ...quickEdit,
              saveImmediately: true,
              mode: 'inline'
            }
          };
      });
    } else if (props.addable || props.editable || isCreateMode) {
      columns = columns.map(column => {
        const quickEdit =
          !isCreateMode && column.hasOwnProperty('quickEditOnUpdate')
            ? column.quickEditOnUpdate
            : column.quickEdit;

        return quickEdit === false
          ? omit(column, ['quickEdit'])
          : {
            ...column,
            quickEdit: {
              ...this.columnToQuickEdit(column),
              ...quickEdit,
              saveImmediately: true,
              mode: 'inline'
            }
          };
      });

      props.editable &&
        btns.push({
          children: ({
            key,
            rowIndex,
            data,
            offset
          }: {
            key: any;
            rowIndex: number;
            data: any;
            offset: number;
          }) =>
            ~this.state.editIndex || (data && data.__isPlaceholder) ? null : (
              <Button
                classPrefix={ns}
                size="sm"
                key={key}
                level="link"
                // tooltip={__('Table.editRow')}
                tooltipContainer={
                  env?.getTopModalContainer || undefined
                }
                onClick={() => {
                  // Jay
                  this.setState({ operationType: 'edit' }, this.startEdit(rowIndex + offset)!)
                }}
              >
                {props.updateBtnLabel || props.editBtnLabel ? (
                  <span>{props.updateBtnLabel || props.editBtnLabel}</span>
                ) : null}
                {/* 兼容之前的写法 */}
                {typeof props.updateBtnIcon !== 'undefined' ? (
                  props.updateBtnIcon ? (
                    <Icon icon={props.updateBtnIcon} className="icon" />
                  ) : null
                ) : props.editBtnIcon ? (
                  <Icon icon={props.editBtnIcon} className="icon" />
                ) : null}
              </Button>
            )
        });

      btns.push({
        children: ({
          key,
          rowIndex,
          offset
        }: {
          key: any;
          rowIndex: number;
          offset: number;
        }) =>
          this.state.editIndex === rowIndex + offset ? (
            <Button
              classPrefix={ns}
              size="sm"
              key={key}
              level="link"
              tooltip={__('save')}
              tooltipContainer={
                env?.getTopModalContainer || undefined
              }
              onClick={this.confirmEdit}
            >
              {props.confirmBtnLabel ? (
                <span>{props.confirmBtnLabel}</span>
              ) : null}
              {props.confirmBtnIcon ? (
                <Icon icon={props.confirmBtnIcon} className="icon" />
              ) : null}
            </Button>
          ) : null
      });

      btns.push({
        children: ({
          key,
          rowIndex,
          offset
        }: {
          key: any;
          rowIndex: number;
          offset: number;
        }) =>
          this.state.editIndex === rowIndex + offset ? (
            <Button
              classPrefix={ns}
              size="sm"
              key={key}
              level="link"
              tooltip={__('cancel')}
              tooltipContainer={
                env?.getTopModalContainer || undefined
              }
              onClick={this.cancelEdit}
            >
              {props.cancelBtnLabel ? (
                <span>{props.cancelBtnLabel}</span>
              ) : null}
              {props.cancelBtnIcon ? (
                <Icon icon={props.cancelBtnIcon} className="icon" />
              ) : null}
            </Button>
          ) : null
      });
    }

    if (props.removable) {
      btns.push({
        children: ({
          key,
          rowIndex,
          data,
          offset
        }: {
          key: any;
          rowIndex: number;
          data: any;
          offset: number;
        }) =>
          (~this.state.editIndex || (data && data.__isPlaceholder)) &&
            needConfirm !== false ? null : (
            // Jay
            // 添加 Popconfirm
            <Popconfirm title={__('Table.deleteRow')}
              okText={__('confirm')} cancelText={__('cancel')}
              onConfirm={() => {
                this.setState({ operationType: 'delete' }, this.removeItem.bind(this, rowIndex + offset))
              }}>
              <Button
                classPrefix={ns}
                size="sm"
                key={key}
                level="link"
                tooltip={__('Table.deleteRow')}
                tooltipContainer={
                  env?.getTopModalContainer || undefined
                }
              // onClick={this.removeItem.bind(this, rowIndex + offset)}
              >
                {props.deleteBtnLabel ? (
                  <span>{props.deleteBtnLabel}</span>
                ) : null}
                {props.deleteBtnIcon ? (
                  <Icon icon={props.deleteBtnIcon} className="icon" />
                ) : null}
              </Button>
            </Popconfirm>
          )
      });
    }

    if (btns.length) {
      let operation = columns.find(item => item.type === 'operation');

      if (!operation) {
        operation = {
          type: 'operation',
          buttons: [],
          label: __('Table.operation'),
          className: 'v-middle nowrap',
          fixed: 'right',
          width: '1%',
          innerClassName: 'm-n'
        };
        columns.push(operation);
      }
      operation.buttons = Array.isArray(operation.buttons)
        ? operation.buttons.concat()
        : [];
      operation.buttons.unshift.apply(operation.buttons, btns);
    }

    if (showIndex) {
      columns.unshift({
        label: __('Table.index'),
        width: '1%',
        children: (props: any) => {
          return <td>{props.offset + props.data.index + 1}</td>;
        }
      });
    }

    return columns;
  }

  columnToQuickEdit(column: any) {
    const quickEdit: any = {
      type: 'input-text'
    };

    if (
      (column.type &&
        /^input\-|(?:select|picker|checkbox|checkboxes|editor|transfer|radios)$/i.test(
          column.type
        )) ||
      ~['textarea', 'combo', 'condition-builder', 'group'].indexOf(column.type)
    ) {
      return {
        ...column,
        label: ''
      };
    }

    return quickEdit;
  }

  // Jay
  // input-table增删改的回调
  handleInputChange(items?: any[]) {
    // onInputTableChange是form(index.tsx)的handleInputTableChange方法
    // onInputTableChange是form(index.tsx)->wrapControl->wrapControl包裹的组件(如input-table)的props
    const { onInputTableChange, name } = this.props
    onInputTableChange?.(this.arrangeData(items), name)
  }
  handleStoreChange(items: any[]) {
    this.props.formStore?.changeValue(this.props.name, items)
  }


  handleTableSave(
    rows: Array<object> | object,
    diff: Array<object> | object,
    rowIndexes: Array<string> | string,
  ) {
    const { perPage } = this.props;

    if (~this.state.editIndex) {
      const items = this.editing ? this.items : this.state.items.concat(); // Jay
      const origin = items[this.state.editIndex];

      if (!origin) {
        return;
      }

      const value: any = {
        ...rows
      };
      this.entries.set(value, this.entries.get(origin) || this.entityId++);
      this.entries.delete(origin);
      items.splice(this.state.editIndex, 1, value);

      this.setState({
        items
      });
      return;
    }

    const page = this.state.page;
    let items = this.editing ? this.items : this.state.items.concat(); // Jay

    if (Array.isArray(rows)) {
      (rowIndexes as Array<string>).forEach((rowIndex, index) => {
        const indexes = rowIndex.split('.').map(item => parseInt(item, 10));

        if (page && page > 1 && typeof perPage === 'number') {
          indexes[0] += (page - 1) * perPage;
        }
        const origin = getTree(items, indexes);

        const data = {
          ...origin,
          ...(diff as Array<object>)[index]
        };

        items = spliceTree(items, indexes, 1, data);
      });
    } else {
      const indexes = (rowIndexes as string)
        .split('.')
        .map(item => parseInt(item, 10));

      if (page && page > 1 && typeof perPage === 'number') {
        indexes[0] += (page - 1) * perPage;
      }

      const origin = getTree(items, indexes);
      const data = {
        ...origin,
        ...diff
      };

      // Jay
      // needConfirm为false的情况下，编辑原有行添加isUpdate的标识
      if (data.isOrigin && !data.isAdd) {
        data.isUpdate = true
      }

      items = spliceTree(items, indexes, 1, data);
      this.entries.set(data, this.entries.get(origin) || this.entityId++);
      // this.entries.delete(origin); // 反正最后都会清理的，先不删了吧。
    }
    // Jay 优化编辑时的性能
    this.props.setEdited?.()
    this.editing = true
    this.items = items
    this.handleInputChange(items)
    this.handleStoreChange(items)

    // this.setState(
    //   {
    //     items
    //   },
    //   () => {
    //     this.emitValue()
    //     // 不改变store.data里的数据了，反正最后都是提交inputTableData里的数据
    //     // 数据太多时更新会很卡，可以进行分页，将perPage调小
    //     // this.props.formStore?.changeValue(this.props.name, items)
    //     // this.handleInputChange()
    //   }
    // );
  }
  // Jay
  items: any[];
  editing: boolean;
  //Aug input-table批量修改
  handleTableBatchEdit(values: any) {
    this.state.items.forEach((item, index) => {
      this.handleTableSave(item, values, index + '')
    })

  }

  handleSaveTableOrder(moved: Array<object>, rows: Array<object>) {
    const { onChange } = this.props;

    onChange(rows.map((item: object) => ({ ...item })));
  }

  handlePageChange(page: number) {
    this.setState({ page });
  }

  removeEntry(entry: any) {
    if (this.entries.has(entry)) {
      this.entries.delete(entry);
    }
  }

  getEntryId(entry: any) {
    if (!this.entries.has(entry)) {
      this.entries.set(entry, this.entityId++);
    }

    return String(this.entries.get(entry));
  }

  // Jay
  // 以下是CRUD.tsx组件渲染工具栏的逻辑

  renderStatistics() {
    const {
      store,
      classnames: cx,
      translate: __,
      alwaysShowPagination
    } = this.props;

    if (store.lastPage <= 1 && !alwaysShowPagination) {
      return null;
    }

    return (
      <div className={cx('Crud-statistics')}>
        {__('CRUD.stat', {
          page: store.page,
          lastPage: store.lastPage,
          total: store.total
        })}
      </div>
    );
  }


  renderFilterToggler() {
    const { store, classnames: cx, translate: __ } = this.props;

    if (!store.filterTogggable) {
      return null;
    }

    return (
      <button
        onClick={() => store.setFilterVisible(!store.filterVisible)}
        className={cx('Button Button--sm Button--default', {
          'is-active': store.filterVisible
        })}
      >
        <Icon icon="filter" className="icon m-r-xs" />
        {__('CRUD.filter')}
      </button>
    );
  }

  renderExportCSV() {
    const {
      store,
      classPrefix: ns,
      classnames: cx,
      translate: __,
      loadDataOnce,
      api
    } = this.props;

    return (
      <Button
        classPrefix={ns}
        onClick={() =>
          store.exportAsCSV({
            loadDataOnce,
            api
          })
        }
        size="sm"
      >
        {__('CRUD.exportCSV')}
      </Button>
    );
  }

  renderToolbar(
    toolbar?: SchemaNode,
    index: number = 0,
    childProps: any = {},
    toolbarRenderer?: (toolbar: SchemaNode, index: number) => React.ReactNode
  ) {
    if (!toolbar) {
      return null;
    }

    const type = (toolbar as Schema).type || toolbar;

    if (type === 'bulkActions' || type === 'bulk-actions') {
    } else if (type === 'filter-toggler') {
      return this.renderFilterToggler();
    } else if (type === 'export-csv') {
      return this.renderExportCSV();
    }
    else if (Array.isArray(toolbar)) {
      const children: Array<any> = toolbar
        .map((toolbar, index) => ({
          dom: this.renderToolbar(toolbar, index, childProps, toolbarRenderer),
          toolbar
        }))
        .filter(item => item.dom);
      const len = children.length;
      const cx = this.props.classnames;
      if (len) {
        return (
          <div className={cx('Crud-toolbar')} key={index}>
            {children.map(({ toolbar, dom: child }, index) => {
              const type = (toolbar as Schema).type || toolbar;
              let align =
                toolbar.align || (type === 'pagination' ? 'right' : 'left');
              return (
                <div
                  key={index}
                  className={cx(
                    'Crud-toolbar-item',
                    align ? `Crud-toolbar-item--${align}` : '',
                    toolbar.className
                  )}
                  onClick={() => {
                    if (!this.state.hasClickedToolbar) {
                      this.setState({ hasClickedToolbar: true })
                    }
                  }}
                >
                  {child}
                </div>
              );
            })}
          </div>
        );
      }
      return null;
    }

    const result = toolbarRenderer
      ? toolbarRenderer(toolbar, index)
      : undefined;

    if (result !== void 0) {
      return result;
    }

    const { render, store } = this.props;
    const $$editable = childProps.$$editable;

    // Jay
    // actionType为dialog和ajax时 不传 handleAction，依然可以生效
    const toolbarProps: any = {}
    if ((toolbar as any).actionType === "export") {
      toolbarProps.onAction = this.handleAction
    }

    return render(`toolbar/${index}`, toolbar, {
      // 包两层，主要是为了处理以下 case
      // 里面放了个 form，form 提交过来的时候不希望把 items 这些发送过来。
      // 因为会把数据呈现在地址栏上。
      data: createObject(
        createObject(store.filterData, {
          items: childProps.items,
          selectedItems: childProps.selectedItems,
          unSelectedItems: childProps.unSelectedItems
        }),
        {}
      ),
      // Jay
      // page: store.page,
      // lastPage: store.lastPage,
      // perPage: store.perPage,
      // total: store.total,
      // onQuery: this.handleQuery,
      // onChangePage: this.handleChangePage,
      // onBulkAction: this.handleBulkAction,
      // onAction: this.handleAction,
      ...toolbarProps,
      $$editable
    });
  }

  renderHeaderToolbar(
    childProps: any,
    toolbarRenderer?: (toolbar: SchemaNode, index: number) => React.ReactNode
  ) {
    let { toolbar, toolbarInline, headerToolbar } = this.props;

    if (toolbar) {
      if (Array.isArray(headerToolbar)) {
        headerToolbar = toolbarInline
          ? headerToolbar.concat(toolbar)
          : [headerToolbar, toolbar];
      } else if (headerToolbar) {
        headerToolbar = [headerToolbar, toolbar];
      } else {
        headerToolbar = toolbar;
      }
    }

    return this.renderToolbar(
      headerToolbar || [],
      0,
      childProps,
      toolbarRenderer
    );
  }

  renderFooterToolbar(
    childProps: any,
    toolbarRenderer?: (toolbar: SchemaNode, index: number) => React.ReactNode
  ) {
    let { toolbar, toolbarInline, footerToolbar } = this.props;

    if (toolbar) {
      if (Array.isArray(footerToolbar)) {
        footerToolbar = toolbarInline
          ? footerToolbar.concat(toolbar)
          : [footerToolbar, toolbar];
      } else if (footerToolbar) {
        footerToolbar = [footerToolbar, toolbar];
      } else {
        footerToolbar = toolbar;
      }
    }

    return this.renderToolbar(footerToolbar, 0, childProps, toolbarRenderer);
  }
  // actionType为dialog和ajax时 不传 handleAction，依然可以生效
  // handleAction 暂时仅支持export
  handleAction(
    e: React.UIEvent<any> | undefined,
    action: Action,
    ctx: any,
    throwErrors: boolean = false,
    delegate?: IScopedContext
  ): any {
    const {
      onAction,
      store,
      messages,
      pickerMode,
      env,
      pageField,
      stopAutoRefreshWhenModalIsOpen
    } = this.props;

    if (action.actionType === 'dialog') {
      // store.setCurrentAction(action);
      // const idx: number = (ctx as any).index;
      // const length = store.items.length;
      // stopAutoRefreshWhenModalIsOpen && clearTimeout(this.timer);
      // store.openDialog(ctx, {
      //   hasNext: idx < length - 1,
      //   nextIndex: idx + 1,
      //   hasPrev: idx > 0,
      //   prevIndex: idx - 1,
      //   index: idx
      // });
    }
    // else if (action.actionType === 'ajax') {
    //   store.setCurrentAction(action);
    //   const data = ctx;

    //   // 由于 ajax 一段时间后再弹出，肯定被浏览器给阻止掉的，所以提前弹。
    //   const redirect = action.redirect && filter(action.redirect, data);
    //   redirect && action.blank && env.jumpTo(redirect, action);

    //   return store
    //     .saveRemote(action.api!, data, {
    //       successMessage:
    //         (action.messages && action.messages.success) ||
    //         (messages && messages.saveSuccess),
    //       errorMessage:
    //         (action.messages && action.messages.failed) ||
    //         (messages && messages.saveFailed)
    //     })
    //     .then(async (payload: object) => {
    //       const data = createObject(ctx, payload);

    //       if (action.feedback && isVisible(action.feedback, data)) {
    //         await this.openFeedback(action.feedback, data);
    //         stopAutoRefreshWhenModalIsOpen && clearTimeout(this.timer);
    //       }

    //       const redirect = action.redirect && filter(action.redirect, data);
    //       redirect && !action.blank && env.jumpTo(redirect, action);
    //       action.reload
    //         ? this.reloadTarget(action.reload, data)
    //         : redirect
    //           ? null
    //           : this.search(undefined, undefined, true, true);
    //       action.close && this.closeTarget(action.close);
    //     })
    //     .catch(() => { });
    // }
    else if (
      pickerMode &&
      (action.actionType === 'confirm' || action.actionType === 'submit')
    ) {
      store.setCurrentAction(action);
      return Promise.resolve({
        items: store.selectedItems.concat()
      });
    }
    // else if (action.actionType === 'label-design') {
    //   templateDesign(ctx, env.fetcher, action.api, this.search, env.getModalContainer)
    // }
    else if (action.actionType === 'export') {
      store.setCurrentAction(action);
      let exportData: any = {};
      let _ids: string = '';
      let _selectedItems: any[] = this.props?.store?.selectedItems?.toJSON();

      _selectedItems.map((_select: any) => {
        _ids += _select.hasOwnProperty(this.props.primaryField) ? _select[this.props.primaryField] + ',' : ''
      });

      exportData = { ...exportData, ...this.props?.store?.query, ...this.props?.storeData };
      exportData.selectedItems = _selectedItems;
      exportData.primaryField = this.props.primaryField;
      exportData.ids = (action?.exportType === 2 ? ctx[this.props.primaryField as any] : _ids.slice(0, _ids.length - 1))
      store.openLionExport(exportData, env, true)
      return false
    }
    else {
      onAction(e, action, ctx, throwErrors, delegate || this.context);
    }
  }

  handleTableRotate = () => {
    const { tableRotate } = this.state;
    this.setState({ tableRotate: !tableRotate });
  }

  render() {
    const {
      className,
      value,
      showAddBtn,
      disabled,
      render,
      placeholder,
      draggable,
      addable,
      columnsTogglable,
      combineNum,
      combineFromIndex,
      translate: __,
      canAccessSuperData,
      expandConfig,
      affixRow,
      prefixRow,
      formInited,
      perPage,
      showIndex,
      classnames: cx
    } = this.props;

    if (formInited === false) {
      return null;
    }

    let items = this.state.items;
    let showPager = false;
    const page = this.state.page || 1;
    let offset = 0;
    let lastPage = 1;
    if (typeof perPage === 'number' && perPage && items.length > perPage) {
      lastPage = Math.ceil(items.length / perPage);
      items = items.slice((page - 1) * perPage, page * perPage);
      showPager = true;
      offset = (page - 1) * perPage;
    }

    return (
      <div className={cx('InputTable', className)}>
        {render(
          'body',
          {
            type: 'table',
            placeholder: __(placeholder),
            columns: this.state.columns,
            affixHeader: false,
            prefixRow,
            affixRow,
            autoFillHeight: this.props.autoFillHeight ?? true
          },
          {
            value: undefined,
            saveImmediately: true,
            disabled,
            draggable: draggable && !~this.state.editIndex,
            items: items,
            getEntryId: this.getEntryId,
            onSave: this.handleTableSave,
            onSaveOrder: this.handleSaveTableOrder,
            onBatchEdit: this.handleTableBatchEdit,
            buildItemProps: this.buildItemProps,
            quickEditFormRef: this.subFormRef,
            columnsTogglable: columnsTogglable,
            combineNum: combineNum,
            combineFromIndex: combineFromIndex,
            expandConfig,
            canAccessSuperData,
            reUseRow: false,
            offset,
            // Jay
            itemBadge: this.props.itemBadge,
            headerToolbarRender: this.renderHeaderToolbar,
            footerToolbarRender: this.renderFooterToolbar,
            tableLayout: this.props.tableLayout,
            tableType: 'input-table',//来自input-table不需要分页，把加载更多按钮去掉
            tableRotate: this.state.tableRotate,
            handleTableRotate: this.handleTableRotate,
            showIndex
          }
        )}
        {(addable && showAddBtn !== false) || showPager ? (
          <div className={cx('InputTable-toolbar')}>
            {addable && showAddBtn !== false ? (
              <Button
                disabled={disabled}
                size="sm"
                onClick={() => this.addItem(this.state.items.length)}
              >
                <Icon icon="plus" className="icon" />
                <span>{__('Combo.add')}</span>
              </Button>
            ) : null}

            {showPager
              ? render(
                'pager',
                {
                  type: 'pagination'
                },
                {
                  activePage: page,
                  lastPage: lastPage,
                  onPageChange: this.handlePageChange,
                  className: 'InputTable-pager'
                }
              )
              : null}
          </div>
        ) : null}
      </div>
    );
  }
}

@FormItem({
  type: 'input-table',
  // Jay
  storeType: CRUDStore.name
})
export class FormTableRenderer extends FormTable { }
