import _defineProperty from "@babel/runtime/helpers/defineProperty";
// Copyright 1999-2023. Plesk International GmbH. All rights reserved.

import React, { Component } from 'react';
import { safeInvoke } from '../utils';
import { isRtl } from '../../utils';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { CLS_PREFIX } from '../../constants';
import { jsx as _jsx } from "react/jsx-runtime";
/**
 * `CodeEditor` component is used for editing text as code with proper color highlighting according to selected programming language.
 * @since 0.0.61
 */
class CodeEditor extends Component {
  constructor() {
    super(...arguments);
    _defineProperty(this, "state", {
      content: this.props.children,
      lineSeparator: this.getLineSeparator(this.props.children),
      options: {
        lineNumbers: true,
        lineWrapping: true,
        matchBrackets: true,
        matchTags: {
          bothTags: true
        },
        indentUnit: 4,
        indentWithTabs: true,
        enterMode: 'keep',
        tabMode: 'shift',
        autoRefresh: true,
        extraKeys: {
          'Ctrl-S': 'save'
        },
        direction: isRtl() ? 'rtl' : 'ltr'
      }
    });
    _defineProperty(this, "handleScroll", () => {
      const {
        onScroll
      } = this.props;
      safeInvoke(onScroll, this.codeMirror.getScrollInfo());
    });
    _defineProperty(this, "handleChange", (doc, changes) => {
      if (this.props.readOnly || changes.origin === 'setValue') {
        return;
      }
      const {
        lineSeparator
      } = this.state;
      const {
        onChange
      } = this.props;
      const content = doc.getValue(lineSeparator);
      this.setState({
        content
      }, () => {
        safeInvoke(onChange, content);
      });
    });
  }
  static getDerivedStateFromProps(_ref, state) {
    let {
      readOnly,
      autofocus,
      options
    } = _ref;
    return {
      options: {
        ...state.options,
        ...options,
        autofocus,
        readOnly
      }
    };
  }
  componentDidMount() {
    import( /* webpackChunkName: "codemirror" */'./CodeMirror').then(_ref2 => {
      let {
        CodeMirror
      } = _ref2;
      if (!this.textareaNode) {
        return;
      }
      this.CodeMirror = CodeMirror;
      const {
        onInit,
        scrollPosition
      } = this.props;
      const {
        options
      } = this.state;
      this.codeMirror = CodeMirror.fromTextArea(this.textareaNode, options);
      this.codeMirror.on('scroll', this.handleScroll);
      if (scrollPosition) {
        this.codeMirror.scrollTo(scrollPosition.left, scrollPosition.top);
      }
      this.updateMode(this.props);
      CodeMirror.commands.save = this.props.onSave;
      CodeMirror.commands.find = CodeMirror.commands.findPersistent;
      CodeMirror.commands.findNext = CodeMirror.commands.findPersistentNext;
      CodeMirror.commands.findPrev = CodeMirror.commands.findPersistentPrev;
      safeInvoke(onInit, this.codeMirror.display.wrapper);
      this.codeMirror.on('change', this.handleChange);
    });
  }
  componentDidUpdate(prevProps) {
    if (!this.codeMirror) {
      return;
    }
    Object.keys(this.state.options).forEach(name => {
      this.codeMirror.setOption(name, this.state.options[name]);
    });
    const {
      children,
      mode,
      fileName,
      autofocus,
      scrollPosition
    } = this.props;
    this.updateMode({
      mode,
      fileName
    });
    const newContent = children || '';
    if (newContent !== prevProps.children && newContent !== this.codeMirror.getValue(this.state.lineSeparator)) {
      const prevScrollPosition = prevProps.scrollPosition || this.codeMirror.getScrollInfo();
      this.codeMirror.setValue(newContent);
      this.codeMirror.scrollTo(prevScrollPosition.left, prevScrollPosition.top);
      if (autofocus) {
        this.codeMirror.focus();
      }
    } else if (scrollPosition !== prevProps.scrollPosition) {
      this.codeMirror.scrollTo(scrollPosition.left, scrollPosition.top);
    }
  }
  componentWillUnmount() {
    if (this.codeMirror) {
      this.codeMirror.off('scroll', this.handleScroll);
      this.codeMirror.toTextArea();
    }
  }
  findModeByFileName(fileName) {
    const modeInfo = this.CodeMirror.findModeByFileName(fileName);
    if (modeInfo) {
      return modeInfo.mode;
    }
    return null;
  }
  updateMode(_ref3) {
    let {
      mode,
      fileName
    } = _ref3;
    if (!mode && fileName) {
      mode = this.findModeByFileName(fileName);
    }
    if (!mode) {
      return;
    }
    this.CodeMirror.loadMode(this.codeMirror, mode);
  }
  getLineSeparator() {
    let content = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
    const sep = /^.*?(\r\n?|\n)/.exec(content);
    return sep && sep[1] ? sep[1] : '\n';
  }
  render() {
    const {
      baseClassName,
      className,
      mode,
      fileName,
      readOnly,
      autofocus,
      children,
      onInit,
      onChange,
      onSave,
      options,
      scrollPosition,
      onScroll,
      ...props
    } = this.props;
    const {
      content
    } = this.state;
    return /*#__PURE__*/_jsx("textarea", {
      style: {
        display: 'none'
      },
      ref: ref => this.textareaNode = ref,
      className: classNames(baseClassName, className),
      defaultValue: content,
      ...props
    });
  }
}
CodeEditor.propTypes = {
  /**
   * The mode to use. When not given, this will default to the first mode that was loaded.
   * @since 0.0.61
   */
  mode: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
  /**
   * CodeEditor can detect mode by file name. Will be ignored if you set mode property.
   * @since 0.0.61
   */
  fileName: PropTypes.string,
  /**
   * Content of the `CodeEditor`.
   * @since 0.0.61
   */
  children: PropTypes.string,
  /**
   * Set read only.
   * @since 0.0.61
   */
  readOnly: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
  /**
   * Set auto focus.
   * @since 0.0.61
   */
  autofocus: PropTypes.bool,
  /**
   * onInit event handler.
   * @since 0.0.61
   */
  onInit: PropTypes.func,
  /**
   * onChange event handler.
   * @since 0.0.61
   */
  onChange: PropTypes.func,
  /**
   * onSave event handler.
   * @since 0.0.61
   */
  onSave: PropTypes.func,
  /**
   * See all supported options in [CodeMirror](https://codemirror.net/doc/manual.html#config) documentation.
   * @since 0.0.61
   */
  options: PropTypes.object,
  /**
   * Set scroll position.
   * @since 1.12.4
   */
  scrollPosition: PropTypes.shape({
    left: PropTypes.number,
    top: PropTypes.number
  }),
  /**
   * onScroll event handler.
   * @since 1.12.4
   */
  onScroll: PropTypes.func,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * @ignore
   */
  baseClassName: PropTypes.string
};
CodeEditor.defaultProps = {
  mode: '',
  fileName: '',
  children: '',
  readOnly: false,
  autofocus: false,
  onInit: null,
  onChange: null,
  onSave: null,
  options: null,
  className: null,
  baseClassName: `${CLS_PREFIX}code-editor`,
  scrollPosition: undefined,
  onScroll: undefined
};
export default CodeEditor;