import React from 'react';
import { Renderer, RendererProps } from '../factory';
import { ServiceStore, IServiceStore } from '../store/service';
import { filter } from '../utils/tpl';
import cx from 'classnames';
import LazyComponent from '../components/LazyComponent';
import { resizeSensor } from '../utils/resize-sensor';
import {
  resolveVariableAndFilter,
  isPureVariable,
  dataMapping
} from '../utils/tpl-builtin';
import {
  isApiOutdated,
  isEffectiveApi,
  normalizeApiResponseData
} from '../utils/api';
import { ScopedContext, IScopedContext } from '../Scoped';
import { createObject, findObjectsWithKey, isMobile, uuid } from '../utils/helper';
import {
  BaseSchema,
  SchemaApi,
  SchemaExpression,
  SchemaFunction,
  SchemaName,
  SchemaTokenizeableString
} from '../Schema';
import { ActionSchema } from './Action';
import { isAlive } from 'mobx-state-tree';
// Jay
import Empty from 'antd/lib/empty';
import { linkJump, ModleHandleClick } from '../utils/utils';
import { domUtils } from '../utils/helper'
import { Action, Api } from '../types';
import { Button } from 'antd';

/**
 * Chart 图表渲染器。
 * 文档：https://baidu.gitee.io/amis/docs/components/carousel
 */
export interface ChartSchema extends BaseSchema {
  /**
   * 指定为 chart 类型
   */
  type: 'chart';

  /**
   * Chart 主题配置
   */
  chartTheme?: any;

  /**
   * 图表配置接口
   */
  api?: SchemaApi;

  /**
   * 是否初始加载。
   * @deprecated 建议直接配置 api 的 sendOn
   */
  initFetch?: boolean;

  /**
   * 是否初始加载用表达式来配置
   * @deprecated 建议用 api.sendOn 属性。
   */
  initFetchOn?: SchemaExpression;

  /**
   * 配置echart的config，支持数据映射。如果用了数据映射，为了同步更新，请设置 trackExpression
   */
  config?: any;

  /**
   * 跟踪表达式，如果这个表达式的运行结果发生变化了，则会更新 Echart，当 config 中用了数据映射时有用。
   */
  trackExpression?: string;

  /**
   * 宽度设置
   */
  width?: number;

  /**
   * 高度设置
   */
  height?: number;

  /**
   * 刷新时间
   */
  interval?: number;

  name?: SchemaName;

  /**
   * style样式
   */
  style?: {
    [propName: string]: any;
  };

  dataFilter?: SchemaFunction;

  source?: SchemaTokenizeableString;

  /**
   * 默认开启 Config 中的数据映射，如果想关闭，请开启此功能。
   */
  disableDataMapping?: boolean;

  /**
   * 点击行为配置，可以用来满足下钻操作等。
   * 这个逻辑领导已经改了，clickAction的位置放到serise困
   */
  // clickAction?: ActionSchema & {
  //   linkId?: string;
  //   linkUrl?: string;
  //   linkTitle?: string;
  //   linkType?: string;
  //   type?: string;
  // };

  /**
   * 默认配置时追加的，如果更新配置想完全替换配置请配置为 true.
   */
  replaceChartOption?: boolean;

  /**
   * 不可见的时候隐藏
   */
  unMountOnHidden?: boolean;
  /**
   * 蒙版图层
   */
  maskImageUrl?: string;

  /**
   * 获取 geo json 文件的地址
   */
  mapURL?: SchemaApi;

  /**
   * 地图名称
   */
  mapName?: string;
}

const EVAL_CACHE: { [key: string]: Function } = {};
/**
 * ECharts 中有些配置项可以写函数，但 JSON 中无法支持，为了实现这个功能，需要将看起来像函数的字符串转成函数类型
 * 目前 ECharts 中可能有函数的配置项有如下：interval、formatter、color、min、max、labelFormatter、pageFormatter、optionToContent、contentToOption、animationDelay、animationDurationUpdate、animationDelayUpdate、animationDuration、position、sort
 * 其中用得最多的是 formatter、sort，所以目前先只支持它们
 * @param config ECharts 配置
 */
function recoverFunctionType(config: object) {
  ['formatter', 'sort', 'renderItem', 'symbolSize', 'valueFormatter'].forEach((key: string) => {
    const objects = findObjectsWithKey(config, key);
    for (const object of objects) {
      const code = object[key];
      if (typeof code === 'string' && code.trim().startsWith('function')) {
        try {
          if (!(code in EVAL_CACHE)) {
            EVAL_CACHE[code] = eval('(' + code + ')');
          }
          object[key] = EVAL_CACHE[code];
        } catch (e) {
          console.warn(code, e);
        }
      }
    }
  });
}

export interface ChartProps
  extends RendererProps,
  Omit<ChartSchema, 'type' | 'className'> {
  chartRef?: (echart: any) => void;
  onDataFilter?: (config: any, echarts: any, data?: any) => any;
  onChartWillMount?: (echarts: any) => void | Promise<void>;
  onChartMount?: (chart: any, echarts: any) => void;
  onChartUnMount?: (chart: any, echarts: any) => void;
  store: IServiceStore;
}

// Jay
interface ChartState {
  hasRender: boolean;
  canvasHigh: string;
  visible: boolean;
  /** 空数据错误文案 */
  errMsg: string;
  showEmpty: boolean,
  showData?: 'table' | 'cross',
}
export class Chart extends React.Component<ChartProps, ChartState> {
  static defaultProps: Partial<ChartProps> = {
    replaceChartOption: false,
    unMountOnHidden: false
  };

  static propsList: Array<string> = [];

  ref: any;
  echarts?: any;
  unSensor: Function;
  pending?: object;
  pendingCtx?: any;
  timer: ReturnType<typeof setTimeout>;
  mounted: boolean;
  reloadCancel?: Function;
  containerRef?: any;
  series?: obj[];
  cacheStyle?: any;
  tableData?: {
    columns: obj[],
    data: obj[],
    rowField?: string
    columnField?: string
    valueField?: string[]
  }
  constructor(props: ChartProps) {
    super(props);

    this.refFn = this.refFn.bind(this);
    this.reload = this.reload.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleExport = this.handleExport.bind(this)
    this.handleDialogConfirm = this.handleDialogConfirm.bind(this);
    this.handleDrawerConfirm = this.handleDrawerConfirm.bind(this);
    this.baseModalConfirm = this.baseModalConfirm.bind(this);
    this.handleDialogClose = this.handleDialogClose.bind(this);
    this.handleDrawerClose = this.handleDrawerClose.bind(this);
    this.loadChartMapData = this.loadChartMapData.bind(this);
    this.mounted = true;
    this.containerRef = React.createRef();
    // this.cacheHeight = 0;
    this.cacheStyle = {};
    props.config && this.renderChart(props.config);

    // Jay
    this.state = {
      hasRender: false,
      canvasHigh: '100%',
      visible: false,
      errMsg: '',
      showEmpty: false,
    }
  }

  componentDidMount() {
    const { api, data, initFetch, source } = this.props;
    // 初始化的时候,默认将外部Panel的loading 给关闭
    this.props.setPanelLoading?.(false);
    if (source && isPureVariable(source)) {
      const ret = resolveVariableAndFilter(source, data, '| raw');
      ret && this.renderChart(ret);
    } else if (api && initFetch !== false) {
      // Jay
      this.props.setPanelLoading?.(true)
      this.reload();
    }
  }

  componentDidUpdate(prevProps: ChartProps) {
    const props = this.props;

    if (isApiOutdated(prevProps.api, props.api, prevProps.data, props.data)) {
      this.reload();
    } else if (props.source && isPureVariable(props.source)) {
      const prevRet = prevProps.source
        ? resolveVariableAndFilter(prevProps.source, prevProps.data, '| raw')
        : null;
      const ret = resolveVariableAndFilter(props.source, props.data, '| raw');

      if (prevRet !== ret) {
        this.renderChart(ret || {});
      }
    } else if (props.config !== prevProps.config) {
      this.renderChart(props.config || {});
    } else if (
      props.config &&
      props.trackExpression &&
      filter(props.trackExpression, props.data) !==
      filter(prevProps.trackExpression, prevProps.data)
    ) {
      this.renderChart(props.config || {});
    } else if (
      isApiOutdated(prevProps.mapURL, props.mapURL, prevProps.data, props.data)
    ) {
      const {source, data, api, config} = props;
      this.loadChartMapData(() => {
        if (source && isPureVariable(source)) {
          const ret = resolveVariableAndFilter(source, data, '| raw');
          ret && this.renderChart(ret);
        } else if (api) {
          this.reload();
        } else if (config) {
          this.renderChart(config || {});
        }
      });
    }
  }

  componentWillUnmount() {
    this.mounted = false;
    clearTimeout(this.timer);
  }

  async loadChartMapData(callBackFn?: () => void) {
    const { env, data } = this.props;
    let { mapName, mapURL, mapUrlType } = this.props;
    if (mapURL && mapName && (window as any).echarts) {
      // mapUrlType 1 svg渲染  0 其他
      const isSvg = Number(mapUrlType) === 1;
      let mapGeoResult;
      this.setState({ hasRender: true });
      if (isPureVariable(mapName)) {
        mapName = resolveVariableAndFilter(mapName, data);
      }
      this.props.setPanelLoading?.(true)
      if (isSvg) {
        fetch(mapURL.url, { headers: {
          token: env.token?.()
        }})
        .then(res => res.text()).then((res) => {
          (window as any).echarts.registerMap(mapName!, {svg: res});
        }).finally(() => {this.props.setPanelLoading?.(false)})
      } else {
        mapGeoResult = await (env.fetcher(mapURL as Api, data).catch(() => ({ data: {}, ok: false })).finally(() => { this.props.setPanelLoading?.(false) }));
        if (!mapGeoResult.ok) {
          console.warn('fetch map geo error ' + mapURL);
        }
        (window as any).echarts.registerMap(mapName!, mapGeoResult?.data ?? {});
      }
    }
    if (callBackFn) {
      callBackFn();
    }
  }

  async handleExport({ api }: { api: SchemaApi }) {
    const res = await this.props.env.fetcher(api)
    if (res.ok && res.data != null) {
      const baseUrl = (this.props.env?.axiosInstance?.defaults?.baseURL ?? res?.reqUrl )?? ''
      const link = document.createElement('a')
      link.href = baseUrl + res.data.fileUrl
      link.download = res.data.fileName
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    }
  }

  handleShowData = (tableSchema: any, mode: 'table' | 'cross') => {
    this.tableData = {
      columns: tableSchema.columns,
      data: tableSchema.data,
      rowField: tableSchema.rowField,
      columnField: tableSchema.columnField,
      valueField: tableSchema.valueField
    }
    this.setState({ showData: mode })
  }

  handleClick(ctx: obj) {
    const { onAction, data } = this.props;
    if(this.series && this.series.length > 0) {
      const clickAction = this.series[ctx.seriesIndex]?.clickAction;
      if(clickAction) {
        onAction && onAction(null, clickAction, createObject(data, ctx.data));
        if(clickAction?.linkId) {
          const dataObj = createObject(data, this.dataset[ctx.dataIndex]);
          linkJump(clickAction?.linkId ?? '', dataObj) && ModleHandleClick({ ...this.props, ...clickAction, value: ctx.value, data: dataObj, handleJump: this.handleJump })
        }
      }
    }
  }

  handleJump = (body: any) => {
    const { store } = this.props;
    const types = isMobile() ? 'drawer' : 'dialog';
    const action: any = {
      type: "action",
      actionType: types,
      close: true
    }
    action[types] = {
      title: body.linkTitle,
      type: types,
      size: body.linkSize ?? 'lg',
      bodyClassName: `overflow-y-auto max-h-nestSide-${types}`,
      className: "h-full",
      actions: [],
      overlay: isMobile() ? false : true,
      body: {
        schemaApi: {
          "method": "get",
          "url": body.linkUrl
        },
        type: "service"
      }
    }
    store.setCurrentAction(action);
    types === 'dialog' ? store.openDialog(body.bodydata) : store.openDrawer(body.bodydata)
  }

  chartContent: any

  refFn = async (ref: any) => {
    this.chartContent = ref // 记录表格体
    const chartRef = this.props.chartRef;
    const { chartTheme, onChartWillMount, onChartUnMount, env, mapName, mapURL } = this.props;
    let onChartMount = this.props.onChartMount;

    if (ref) {
      // Promise.all([
      //   import('echarts'),
      //   import('echarts-stat'),
      //   import('echarts/extension/dataTool'),
      //   import('echarts/extension/bmap/bmap')
      // ]).then(async ([echarts, ecStat]) => {
      //   (window as any).echarts = echarts;
      //   (window as any).ecStat = ecStat;

      if (mapURL && mapName) {
          await this.loadChartMapData();
      }
      let theme = 'default';

      if (chartTheme) {
        echarts.registerTheme('custom', chartTheme);
        theme = 'custom';
      }

      if (onChartWillMount) {
        await onChartWillMount(echarts);
      }

      // (echarts as any).registerTransform(
      //   (ecStat as any).transform.regression
      // );
      // (echarts as any).registerTransform((ecStat as any).transform.histogram);
      // (echarts as any).registerTransform(
      //   (ecStat as any).transform.clustering
      // );

      if (env.loadChartExtends) {
        await env.loadChartExtends();
      }
      const darkTheme = localStorage.getItem('g_user_skin');
      if(darkTheme === 'dark') {
        theme = 'dark'
      }
      this.echarts = (echarts as any).init(ref, theme);
      if (typeof onChartMount === 'string') {
        onChartMount = new Function('chart', 'echarts') as any;
      }

      onChartMount?.(this.echarts, echarts);
      this.unSensor = resizeSensor(ref, () => {
        const width = ref.offsetWidth;
        const height = ref.offsetHeight;
        this.echarts?.resize({
          width,
          height
        });
      });

      chartRef && chartRef(this.echarts);
      this.renderChart();
      // });
    } else {
      chartRef && chartRef(null);
      this.unSensor && this.unSensor();

      if (this.echarts) {
        onChartUnMount?.(this.echarts, (window as any).echarts);
        this.echarts.dispose();
        delete this.echarts;
      }
    }

    this.ref = ref;
  }

  reload(subpath?: string, query?: any) {
    const { api, env, store, interval, translate: __ } = this.props;
    if (query) {
      return this.receive(query);
    } else if (!env || !env.fetcher || !isEffectiveApi(api, store.data)) {
      return;
    }

    clearTimeout(this.timer);
    if (this.reloadCancel) {
      this.reloadCancel();
      delete this.reloadCancel;
      this.echarts?.hideLoading();
    }
    this.echarts?.showLoading();
    // 请求Chart数据的时候，关闭Panel的loading
    this.props.setPanelLoading?.(false) // Jay

    store.markFetching(true);
    env
      .fetcher(api, store.data, {
        cancelExecutor: (executor: Function) => (this.reloadCancel = executor)
      })
      .then(result => {
        // this.props.setPanelLoading?.(false) // Jay
        isAlive(store) && store.markFetching(false);
        if (!result.ok) {
          this.renderChart({});
          // 将后端返回的错误渲染到空数据中
          this.setState({errMsg: result.msg});
          return env.notify(
            'error',
            result.msg || __('fetchFailed'),
            result.msgTimeout !== undefined
              ? {
                closeButton: true,
                timeout: result.msgTimeout
              }
              : undefined
          );
        }
        delete this.reloadCancel;

        const data = normalizeApiResponseData(result.data);
        if(data.series) {
          this.series = data.series
        }
        this.setState({ errMsg: '', showEmpty: result.data == null });
        // 说明返回的是数据接口。
        if (!data.series && this.props.config) {
          const ctx = createObject(this.props.data, data);
          this.renderChart(this.props.config, ctx);
        } else {
          this.renderChart(result.data || {});
        }

        this.echarts?.hideLoading();

        interval &&
          this.mounted &&
          (this.timer = setTimeout(this.reload, Math.max(interval, 1000)));
      })
      .catch(reason => {
        // this.props.setPanelLoading?.(false) // Jay
        if (env.isCancel(reason)) {
          return;
        }

        isAlive(store) && store.markFetching(false);
        env.notify('error', reason);
        this.echarts?.hideLoading();
      });
  }

  receive(data: object) {
    const store = this.props.store;

    store.updateData(data);
    this.reload();
  }

  dataset: obj[] = [];

  renderChart(config?: any, data?: any) {
    config && (this.pending = config);
    data && (this.pendingCtx = data);

    if (!this.echarts) {
      return;
    }

    const store = this.props.store;
    let onDataFilter = this.props.onDataFilter;
    const dataFilter = this.props.dataFilter;

    if (!onDataFilter && typeof dataFilter === 'string') {
      onDataFilter = new Function(
        'config',
        'echarts',
        'data',
        dataFilter
      ) as any;
    }

    config = config || this.pending;
    data = data || this.pendingCtx || this.props.data;

    if (!config?.dataset) {
      config = {
        ...config,
        dataset: { source: [] }
      }
    }
    if (typeof config === 'string') {
      config = new Function('return ' + config)();
    }
    try {
      onDataFilter &&
        (config =
          onDataFilter(config, (window as any).echarts, data) || config);
    } catch (e) {
      console.warn(e);
    }

    if (config) {
      try {
        if (!this.props.disableDataMapping) {
          config = dataMapping(
            config,
            data,
            (key: string, value: any) =>
              typeof value === 'function' ||
              (typeof value === 'string' && value.startsWith('function'))
          );
        }
        if (config.toolbox?.feature?.myExport) {
          config.toolbox.feature.myExport.icon = `path://M997.910386 1023.826532H25.812065C11.622368 1023.826532 0 1012.204164 0 997.99712V25.933493C0 11.726449 11.622368 0.104081 25.812065 0.104081c14.207044 0 25.829412 11.622368 25.829412 25.829412v946.234215h946.268909c14.207044 0 25.829412 11.622368 25.829412 25.829412 0 14.207044-11.622368 25.829412-25.829412 25.829412z m-117.52469-448.050956v292.710203c0 14.207044-11.622368 25.829412-25.829412 25.829411-14.189697 0-25.812065-11.622368-25.812065-25.829411V575.775576c0-14.207044 11.622368-25.829412 25.812065-25.829412 14.207044 0 25.829412 11.622368 25.829412 25.829412zM650.054649 463.853907c14.207044 0 25.812065 11.622368 25.812065 25.829412v378.80246c0 14.207044-11.605021 25.829412-25.812065 25.829411-14.189697 0-25.812065-11.622368-25.812065-25.829411V489.683319c0-14.207044 11.622368-25.829412 25.812065-25.829412z m-182.766072-77.470888v482.10276c0 14.207044-11.622368 25.829412-25.812065 25.829411-14.207044 0-25.829412-11.622368-25.829411-25.829411V386.383019c0-14.207044 11.622368-25.829412 25.829411-25.829412 14.189697 0 25.812065 11.622368 25.812065 25.829412z m-232.360624 163.563145c14.189697 0 25.812065 11.622368 25.812065 25.829412v292.710203c0 14.207044-11.622368 25.829412-25.812065 25.829411-14.207044 0-25.812065-11.622368-25.812065-25.829411V575.775576c0-14.207044 11.605021-25.829412 25.812065-25.829412z m418.405245-290.229608c-6.86934 5.585675-15.681523 6.557097-23.609019 4.19793-4.458132 0.884688-9.072386 1.040809-13.634599-0.607139l-207.589368-75.077027-162.192746 93.083024a25.864105 25.864105 0 0 1-34.554861-11.761143 25.881452 25.881452 0 0 1 11.761142-34.572207l171.941658-98.686047c7.545866-3.729566 15.907032-2.983653 22.915146 0.589792l214.424015 77.540276 226.54944-97.95748a25.898799 25.898799 0 0 1 36.324236 3.781606 25.916146 25.916146 0 0 1-3.764259 36.324236l-238.570785 103.144179z`
          config.toolbox.feature.myExport.onclick = () => { this.handleExport(config.toolbox.feature.myExport) }
        }
        if (config.toolbox?.feature?.myToolShowTable) {
          config.toolbox.feature.myToolShowTable.title = config.toolbox.feature.myToolShowTable.title || '数据视图'
          config.toolbox.feature.myToolShowTable.icon = `M896 402.4V237.76C896 177.12 846.88 128 786.24 128H237.76C177.12 128 128 177.12 128 237.76v548.48C128 846.88 177.12 896 237.76 896h548.48c60.64 0 109.76-49.12 109.76-109.76V402.4zM198.88 198.88c10.24-10.4 24.32-16.16 38.88-16.16h548.64a54.72 54.72 0 0 1 54.72 54.88v109.92H182.88v-109.76c0-14.56 5.76-28.64 16.16-38.88z m230.72 395.52v-192h164.8v192h-164.8z m164.8 54.88v192h-164.8v-192h164.8z m-219.52-54.88h-192v-192h192v192zM199.04 825.28a54.224 54.224 0 0 1-16.16-38.72v-137.28h192v192h-136.96c-14.56 0-28.64-5.76-38.88-16z m642.24-38.72a54.56 54.56 0 0 1-54.56 54.72h-137.44v-192h192v137.28z m0-192h-192v-192h192v192z`
          config.toolbox.feature.myToolShowTable.onclick = () => { this.handleShowData(config.toolbox.feature.myToolShowTable, 'table') }
        }
        if (config.toolbox?.feature?.myToolShowTransposeTable) {
          config.toolbox.feature.myToolShowTransposeTable.title = config.toolbox.feature.myToolShowTransposeTable.title || '数据视图转置'
          config.toolbox.feature.myToolShowTransposeTable.icon = `M210.688 117.333333c90.666667-8.021333 162.645333 90.666667 128 178.688-4.864 16.938667-5.290667 25.088 6.656 36.48l4.010667 3.498667c29.312 24.021333 50.645333 61.354667 82.645333 74.666667 32 13.354667 72.021333 2.688 109.354667 2.688h122.624c9.514667 0 16.896-2.133333 22.144-11.946667l1.877333-4.053333c24.021333-58.709333 85.333333-93.354667 146.688-80.042667 58.666667 10.666667 103.978667 69.333333 101.333333 133.333333 0 61.354667-45.354667 117.333333-104.021333 128-61.354667 10.666667-122.666667-18.645333-146.688-79.957333-5.333333-13.354667-13.312-16.042667-26.666667-16.042667h-195.2c-11.392 0.170667-18.133333 1.578667-18.133333 18.688v130.688c0 13.312 5.376 18.645333 18.688 23.978667 58.666667 26.666667 90.666667 85.333333 77.354667 149.333333-13.354667 58.666667-66.688 104.021333-128 104.021334-61.354667 0-114.688-45.354667-128-106.666667-10.24-56.149333 18.602667-117.12 72.490666-143.36l7.466667-3.328c13.354667-5.333333 16.042667-13.354667 16.042667-26.666667v-146.688c0-15.957333-2.688-23.978667-13.354667-34.645333C341.333333 426.666667 320 402.645333 295.978667 378.666667c-7.082667-9.472-14.208-12.629333-25.045334-9.472l-4.266666 1.450666C213.333333 392.021333 165.333333 384 125.312 344.021333 85.333333 304 77.354667 256 96 205.354667c18.688-53.333333 58.666667-82.688 114.688-88.021334z m-42.666667 298.666667c26.624 32 53.333333 61.354667 79.957334 96-21.333333 0-48-5.333333-58.666667 2.688-10.666667 13.312-2.645333 37.290667-2.645333 58.624v149.333333c0 16 2.688 21.333333 18.688 18.688l11.605333 0.042667c9.984 0.170667 21.12 0.725333 36.394667 2.602667l-34.133334 40.533333C202.666667 804.266667 186.666667 823.466667 170.666667 842.666667l-65.365334-78.165334-17.28-20.48h47.957334c10.666667 0 16.042667-2.688 16.042666-16v-197.333333c0-10.666667-2.688-16.042667-16.042666-16.042667H85.333333l50.474667-60.074666 32.213333-38.570667z m247.978667 293.333333c-34.688 0-64 32-64 66.688 0 34.645333 26.666667 64 64 64 34.688 0 64-29.354667 64-64 0-37.333333-29.354667-66.688-64-66.688zM810.666667 381.312c-34.688 0-66.688 29.354667-66.688 64 0 34.688 29.354667 66.688 64 66.688 34.688 0 66.688-29.354667 69.333333-66.688 0-34.645333-32-64-66.645333-64zM215.978667 184.064c-34.645333 2.645333-64 32-64 66.645333 2.688 34.688 32 64 66.688 64s66.688-29.354667 64-66.688c0-37.333333-29.354667-64-66.688-64z m493.354666-64c26.666667 18.645333 48 37.290667 69.333334 55.978667l9.130666 7.68c19.797333 17.237333 16.768 21.802667-9.130666 43.008-21.333333 16-42.666667 34.645333-69.333334 55.978666v-53.333333c0-10.666667-2.688-13.354667-13.354666-13.354667h-199.978667c-13.354667 0-16 8.021333-16 18.688v48c-32-29.354667-64-53.333333-88.021333-72.021333-7.978667-7.978667 0-13.312 5.376-16 26.624-21.333333 53.333333-45.312 82.645333-69.333333l2.090667 4.394666a6.4 6.4 0 0 1 0.554666 2.304v1.322667c0 16-7.978667 40.021333 2.688 50.688s34.688 2.645333 53.333334 2.645333h152.021333c16 0 18.645333-2.688 18.645333-18.688V120.021333z`
          config.toolbox.feature.myToolShowTransposeTable.onclick = () => { this.handleShowData(config.toolbox.feature.myToolShowTransposeTable, 'cross') }
        }
        const chartObj = config.series?.[0]
        if(chartObj) {
          this.dataset = chartObj.type === 'scatter' ? chartObj.data.map((item: any[]) => {
            const jumpData = item[item.length - 1];
            if(typeof jumpData == 'object') return jumpData;
            return {};
          }) : (chartObj.data?.length > 0 ? chartObj.data : config.dataset.source);
        }
        recoverFunctionType(config!);

        if (isAlive(store) && store.loading) {
          this.echarts?.showLoading();
        } else {
          this.echarts?.hideLoading();
        }
        this.setState({ hasRender: true }) // Jay
        // 强制重绘
        this.echarts.clear();
        if (this.props.maskImageUrl) {
          const maskImage = new Image();
          maskImage.src = this.props.maskImageUrl;
          config = {
            ...config,
            maskImage
          }
        }
        this.echarts?.setOption(config!, this.props.replaceChartOption);
        this.echarts.on('click', this.handleClick);
      } catch (e) {
        console.warn(e);
      }
    }
  }

  async handleDialogConfirm(
    values: object[],
    action: Action,
    ctx: any,
    components: Array<any>,
  ) {
    return await this.baseModalConfirm('dialog')(values, action, ctx,
      components)
  }

  async handleDrawerConfirm(
    values: object[],
    action: Action,
    ctx: any,
    components: Array<any>,
  ) {
    return await this.baseModalConfirm('drawer')(values, action, ctx,
      components)
  }

  baseModalConfirm(type: 'drawer' | 'dialog') {
    return async (values: object[],
      action: Action,
      ctx: any,
      components: Array<any>) => {
      const {
        store,
        pageField,
        stopAutoRefreshWhenModalIsOpen,
        interval,
        silentPolling,
        env
      } = this.props;
      switch (type) {
        case 'drawer':
        default:
          store.closeDrawer(true);
          break
        case 'dialog':
          store.closeDialog(true);
          break;
      }
      // const dialogAction = store.action as Action;
      // const reload = action.reload ?? dialogAction.reload;
      // if (reload) {
      //   this.reloadTarget(reload, ctx);
      // }
      // let redirect = action.redirect ?? dialogAction.redirect;
      // redirect = redirect && filter(redirect, ctx);
      // redirect && env.jumpTo(redirect, dialogAction);
    }
  }
  handleDialogClose(
    confirmed?: boolean,
    formInstance?: any,
  ) {
    return this.handleBaseModalClose('dialog')(confirmed, formInstance)
  }

  handleDrawerClose(
    confirmed?: boolean,
    formInstance?: any,
  ) {
    return this.handleBaseModalClose('drawer')(confirmed, formInstance)
  }


  handleBaseModalClose(type: 'dialog' | 'drawer') {
    return (confirmed?: boolean, formInstance?: any) => {
      const { translate: __, store } = this.props;
      switch (type) {
        case 'dialog':
          store.closeDialog();
          break;
        case 'drawer':
          store.closeDrawer();
          break;
      }
    }
  }

  handleLoopGetParentHeight(currentDom: any): {width: string, height: number} {
    const closetParent = currentDom.parentElement ?? {};
    const { clientHeight, clientWidth } = closetParent;
    if (clientHeight) {
      // 防止滚动条影响
      return {height: Number.parseInt(Number(clientHeight - 2).toString()), width: `${clientWidth - 4}px`}
    } else {
      return this.handleLoopGetParentHeight(closetParent)
    }
  }
  getDomStyle() {
    let style = this.props.style || {};

    const { height, width, classPrefix: ns, } = this.props;

    // requestAnimationFrame(() => {

    //   if (!height && this.containerRef.current) {
    //     if (this.cacheHeight) {
    //       style.height = this.cacheHeight;
    //     } else {
    //       const parentDomHeight = this.handleLoopGetParentHeight(this.containerRef.current);
    //       this.cacheHeight = parentDomHeight;
    //       style.height = parentDomHeight;
    //     }
    //   }
    //   return style;
    // })
    style.width = '100%';
    if (!height && this.containerRef.current) {
      if (this.cacheStyle?.height) {
        style.height = this.cacheStyle.height;
      } else {
        const {height: parentDomHeight, width: parentDomWidth} = this.handleLoopGetParentHeight(this.containerRef.current);
        // const gridHeight = domUtils.closest(this.containerRef, `${ns}Grid`)?.clientHeight;
        // if (parentDomHeight) {

        // }
          // this.cacheHeight = parentDomHeight;
          style.height = parentDomHeight;
          style.width = parentDomWidth;
      }
    } else {
      style.height = height;
    }
    style.width = width || style.width;
    // style.margin = '0 auto';
    this.cacheStyle = {
      height: style.height,
      width: `calc(${width || '100%'} - 6px)`,
      margin: '0 auto'
    };

    return style;
  }
  render() {
    const {
      className,
      width,
      height,
      classPrefix: ns,
      unMountOnHidden,
      render,
      store
    } = this.props;
    let style = this.props.style || {};

    width && (style.width = width);
    height && (style.height = height);
    // 新增未配置高度的情况下，自动获取父/祖先 容器高度
    // requestAnimationFrame(() => {

    //   if (!height && this.containerRef.current) {
    //     if (this.cacheHeight) {
    //       style.height = this.cacheHeight;
    //     } else {
    //       const parentDomHeight = this.handleLoopGetParentHeight(this.containerRef.current);
    //       this.cacheHeight = parentDomHeight;
    //       style.height = parentDomHeight;
    //     }
    //   }
    // })
    return (
      <div className={cx(`${ns}Chart`, className)} ref={this.containerRef} style={{  ...this.cacheStyle }}>
        <LazyComponent
          unMountOnHidden={unMountOnHidden}
          placeholder="..." // 之前那个 spinner 会导致 sensor 失效
          component={() => (
            <>
              {!this.state.showEmpty && <div className={`${ns}Chart-content`} ref={this.refFn} style={{  ...(this.getDomStyle()) }}></div>}
              {/* Jay */}
              {(!this.state.hasRender || this.state.showEmpty) &&
                <div style={{
                  display: 'flex', justifyContent: 'center',
                  alignItems: 'center', width: '100%', height: '100%', minHeight: 300, whiteSpace: 'normal'
                }}>
                  <Empty description={this.state.errMsg?.length ? this.state.errMsg : (this.props.translate(this.props.placeholder || 'placeholder.noData'))} />
                </div>}
            </>
          )}
        />
        {
          this.state.showData && <div className={cx(`${ns}Chart-table`)}>
            <div className='chart-table-body'>
            {
              render('', {
                type: "crud",
                mode: this.state.showData === 'cross' ? 'cross' : undefined,
                cross: this.state.showData === 'cross' ? {
                  "valueFields": this.tableData?.valueField?.join(','),
                  "positionType": 1,
                  "rowFields": [{ "name": this.tableData?.rowField }],
                  "columnFields": [{ "name": this.tableData?.columnField}]
                } : undefined,
                data: {
                  total: this.tableData?.data.length,
                  items: this.tableData?.data
                },
                aliasTitle: '数据试图',
                loadDataOnce: true,
                "name": `${uuid()}`,
                "affixHeader": true,
                "columns": this.tableData?.columns.map(item => {
                  const filteredEntries = Object.entries(item).filter(([key, value]) => value !== null);
                  // 将过滤后的键值对数组转换回对象
                  return Object.fromEntries(filteredEntries);
                }),
                "filterTogglable": false,
                source: "${items}",
                "autoFillHeight": false,
                "multiple": false,
                "setBorder": true,
                "keepItemSelectionOnPageChange": false,
                "syncLocation": false,
                "checkOnItemClick": false,
                "showIndex": false,
                "header": [],
                "footer": [],
                // headerToolbar: [{
                //     "type": "data-cross",
                //     "label": "交叉制表",
                //     "icon": "#icon-toolcross",
                //     "align": "right"
                // }],
                footerToolbar: this.state.showData === 'table' ? [
                  {
                    "type": "pagination",
                    "align": "right"
                  },
                  {
                    "type": "switch-per-page",
                    "align": "right"
                  },
                  {
                    "type": "statistics",
                    "align": "right"
                  }
                ] : [],
                isStatic: this.state.showData === 'table',
                perPage: this.state.showData === 'table' ? 100 : 2147483647,
                perPageAvailable: [100, 200, 500, 1000]
              })
            }
            </div>
            <div className='chart-table-footer'>
              <Button danger type='primary' size='small' onClick={() => this.setState({showData: undefined})}>关闭</Button>
            </div>
          </div>
        }
        {
          this.props.store.dialogOpen ?
            render(
              'dialog',
              {
                ...((store.action as Action) &&
                  ((store.action as Action).dialog as object)),
                type: 'dialog'
              },
              {
                key: 'dialog',
                data: store.dialogData,
                onConfirm: this.handleDialogConfirm,
                onClose: this.handleDialogClose,
                show: store.dialogOpen,
                replace: store.replace
              }
            ) : null
        }
        {
          this.props.store.drawerOpen ?
            render(
              'drawer',
              {
                ...((store.action as Action) &&
                  ((store.action as Action).drawer as object)),
                type: 'drawer'
              },
              {
                key: 'drawer',
                data: store.drawerData,
                onConfirm: this.handleDrawerConfirm,
                onClose: this.handleDrawerClose,
                show: store.drawerOpen,
                // onAction: this.handleAction,
              }
            ) : null
        }
      </div>
    );
  }
}

@Renderer({
  type: 'chart',
  storeType: ServiceStore.name
})
export class ChartRenderer extends Chart {
  static contextType = ScopedContext;

  constructor(props: ChartProps, context: IScopedContext) {
    super(props);

    const scoped = context;
    scoped.registerComponent(this);
  }

  componentWillUnmount() {
    super.componentWillUnmount();
    const scoped = this.context as IScopedContext;
    scoped.unRegisterComponent(this);
  }
}
