"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.useExpressionRenderer = useExpressionRenderer;
var _react = require("react");
var _operators = require("rxjs/operators");
var _useUpdateEffect = _interopRequireDefault(require("react-use/lib/useUpdateEffect"));
var _common = require("../../common");
var _loader = require("../loader");
var _use_debounced_value = require("./use_debounced_value");
var _use_shallow_memo = require("./use_shallow_memo");
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

function useExpressionRenderer(nodeRef, {
  debounce,
  expression,
  hasCustomErrorRenderer,
  onData$,
  onEvent,
  onRender$,
  reload$,
  ...loaderParams
}) {
  const [{
    error,
    isEmpty,
    isLoading
  }, setState] = (0, _react.useReducer)((currentState, newState) => ({
    ...currentState,
    ...newState
  }), {
    isEmpty: true,
    isLoading: false,
    error: null
  });
  const memoizedOptions = (0, _use_shallow_memo.useShallowMemo)({
    expression,
    params: (0, _use_shallow_memo.useShallowMemo)(loaderParams)
  });
  const [{
    expression: debouncedExpression,
    params: debouncedLoaderParams
  }, isDebounced] = (0, _use_debounced_value.useDebouncedValue)(memoizedOptions, debounce);
  const expressionLoaderRef = (0, _react.useRef)(null);

  // flag to skip next render$ notification,
  // because of just handled error
  const hasHandledErrorRef = (0, _react.useRef)(false);
  // will call done() in LayoutEffect when done with rendering custom error state
  const errorRenderHandlerRef = (0, _react.useRef)(null);

  /* eslint-disable react-hooks/exhaustive-deps */
  // OK to ignore react-hooks/exhaustive-deps because options update is handled by calling .update()
  (0, _react.useEffect)(() => {
    var _expressionLoaderRef$;
    expressionLoaderRef.current = nodeRef.current && new _loader.ExpressionLoader(nodeRef.current, debouncedExpression, {
      ...debouncedLoaderParams,
      // react component wrapper provides different
      // error handling api which is easier to work with from react
      // if custom renderError is not provided then we fallback to default error handling from ExpressionLoader
      onRenderError: (domNode, newError, handlers) => {
        var _debouncedLoaderParam;
        errorRenderHandlerRef.current = handlers;
        setState({
          error: newError,
          isEmpty: false,
          isLoading: false
        });
        return (_debouncedLoaderParam = debouncedLoaderParams.onRenderError) === null || _debouncedLoaderParam === void 0 ? void 0 : _debouncedLoaderParam.call(debouncedLoaderParams, domNode, newError, handlers);
      }
    });
    const subscription = (_expressionLoaderRef$ = expressionLoaderRef.current) === null || _expressionLoaderRef$ === void 0 ? void 0 : _expressionLoaderRef$.loading$.subscribe(() => {
      hasHandledErrorRef.current = false;
      setState({
        isLoading: true
      });
    });
    return () => {
      var _expressionLoaderRef$2;
      subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe();
      (_expressionLoaderRef$2 = expressionLoaderRef.current) === null || _expressionLoaderRef$2 === void 0 ? void 0 : _expressionLoaderRef$2.destroy();
      expressionLoaderRef.current = null;
      errorRenderHandlerRef.current = null;
    };
  }, [debouncedLoaderParams.onRenderError, debouncedLoaderParams.interactive, debouncedLoaderParams.renderMode, debouncedLoaderParams.syncColors, debouncedLoaderParams.syncTooltips, debouncedLoaderParams.syncCursor]);
  (0, _react.useEffect)(() => {
    var _expressionLoaderRef$3;
    const subscription = onEvent && ((_expressionLoaderRef$3 = expressionLoaderRef.current) === null || _expressionLoaderRef$3 === void 0 ? void 0 : _expressionLoaderRef$3.events$.subscribe(onEvent));
    return () => subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe();
  }, [expressionLoaderRef.current, onEvent]);
  (0, _react.useEffect)(() => {
    var _expressionLoaderRef$4;
    const subscription = (_expressionLoaderRef$4 = expressionLoaderRef.current) === null || _expressionLoaderRef$4 === void 0 ? void 0 : _expressionLoaderRef$4.data$.subscribe(({
      partial,
      result
    }) => {
      var _expressionLoaderRef$5;
      setState({
        isEmpty: false,
        ...(!(0, _common.isExpressionValueError)(result) ? {
          error: null
        } : {})
      });
      onData$ === null || onData$ === void 0 ? void 0 : onData$(result, (_expressionLoaderRef$5 = expressionLoaderRef.current) === null || _expressionLoaderRef$5 === void 0 ? void 0 : _expressionLoaderRef$5.inspect(), partial);
    });
    return () => subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe();
  }, [expressionLoaderRef.current, onData$]);
  (0, _react.useEffect)(() => {
    var _expressionLoaderRef$6;
    const subscription = (_expressionLoaderRef$6 = expressionLoaderRef.current) === null || _expressionLoaderRef$6 === void 0 ? void 0 : _expressionLoaderRef$6.render$.pipe((0, _operators.filter)(() => !hasHandledErrorRef.current)).subscribe(item => {
      setState({
        error: null,
        isEmpty: false,
        isLoading: false
      });
      onRender$ === null || onRender$ === void 0 ? void 0 : onRender$(item);
    });
    return () => {
      subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe();
    };
  }, [expressionLoaderRef.current, onRender$]);
  /* eslint-enable react-hooks/exhaustive-deps */

  (0, _react.useEffect)(() => {
    const subscription = reload$ === null || reload$ === void 0 ? void 0 : reload$.subscribe(() => {
      var _expressionLoaderRef$7;
      (_expressionLoaderRef$7 = expressionLoaderRef.current) === null || _expressionLoaderRef$7 === void 0 ? void 0 : _expressionLoaderRef$7.update(debouncedExpression, debouncedLoaderParams);
    });
    return () => subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe();
  }, [reload$, debouncedExpression, debouncedLoaderParams]);
  (0, _useUpdateEffect.default)(() => {
    var _expressionLoaderRef$8;
    (_expressionLoaderRef$8 = expressionLoaderRef.current) === null || _expressionLoaderRef$8 === void 0 ? void 0 : _expressionLoaderRef$8.update(debouncedExpression, debouncedLoaderParams);
  }, [debouncedExpression, debouncedLoaderParams]);

  // call expression loader's done() handler when finished rendering custom error state
  (0, _react.useLayoutEffect)(() => {
    if (error && hasCustomErrorRenderer) {
      var _errorRenderHandlerRe;
      hasHandledErrorRef.current = true;
      (_errorRenderHandlerRe = errorRenderHandlerRef.current) === null || _errorRenderHandlerRe === void 0 ? void 0 : _errorRenderHandlerRe.done();
    }
    errorRenderHandlerRef.current = null;
  }, [error, hasCustomErrorRenderer]);
  return {
    error,
    isEmpty,
    isLoading: isLoading || isDebounced
  };
}