text: // @flow
  import * as React from 'react';
  import classNames from 'classnames';
  import ExpandMore from '@material-ui/icons/ExpandMore';
  import ExpandLess from '@material-ui/icons/ExpandLess';
  import Button from '@material-ui/core/Button';
  import InlinePopover from '../../InlinePopover';
  import ObjectField from '../../ParameterFields/ObjectField';
  import {
    largeSelectedArea,
    largeSelectableArea,
    selectableArea,
  } from '../ClassNames';
  import { getHelpLink } from '../../../Utils/HelpLink';
  import { type EventRendererProps } from './EventRenderer';
  import Measure from 'react-measure';
  import { CodeEditor } from '../../../CodeEditor';
  import { shouldActivate } from '../../../UI/KeyboardShortcuts/InteractionKeys';
  import { Trans } from '@lingui/macro';
  const gd: libGDevelop = global.gd;
  
  const fontFamily = '"Lucida Console", Monaco, monospace';
  const MINIMUM_EDITOR_HEIGHT = 200;
  const EDITOR_PADDING = 100;
  
  const styles = {
    container: {
      minHeight: 30,
      display: 'flex',
      flexDirection: 'column',
      backgroundColor: '#1e1e1e',
    },
    wrappingText: {
      fontFamily,
      fontSize: '0.95em',
      paddingLeft: 5,
      paddingRight: 5,
      paddingTop: 2,
      paddingBottom: 2,
      margin: 0,
      backgroundColor: '#1e1e1e',
      color: '#d4d4d4',
      overflowX: 'hidden',
      maxWidth: '100%',
    },
    comment: {
      color: '#777',
    },
    commentLink: {
      cursor: 'pointer',
      color: '#777',
      textDecoration: 'underline',
    },
    expandIcon: {
      color: '#d4d4d4',
    },
  };
  
  type State = {|
    editingObject: boolean,
    editingPreviousValue: ?string,
    anchorEl: ?any,
  |};
  
  export default class JsCodeEvent extends React.Component<
    EventRendererProps,
    State
  > {
    _objectField: ?ObjectField = null;
    state = {
      editingObject: false,
      editingPreviousValue: null,
      anchorEl: null,
    };
  
    _input: ?any;
  
    onChange = (newValue: string) => {
      const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
      jsCodeEvent.setInlineCode(newValue);
    };
  
    editObject = (domEvent: any) => {
      const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
      const parameterObjects = jsCodeEvent.getParameterObjects();
  
      // We should not need to use a timeout, but
      // if we don't do this, the InlinePopover's clickaway listener
      // is immediately picking up the event and closing.
      // Search the rest of the codebase for inlinepopover-event-hack
      const anchorEl = domEvent.currentTarget;
      setTimeout(
        () =>
          this.setState(
            {
              editingObject: true,
              editingPreviousValue: parameterObjects,
              anchorEl,
            },
            () => {
              // Give a bit of time for the popover to mount itself
              setTimeout(() => {
                if (this._objectField) this._objectField.focus();
              }, 10);
            }
          ),
        10
      );
    };
  
    cancelObjectEditing = () => {
      this.endObjectEditing();
  
      const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
      const { editingPreviousValue } = this.state;
      if (editingPreviousValue != null) {
        jsCodeEvent.setParameterObjects(editingPreviousValue);
        this.forceUpdate();
      }
    };
  
    endObjectEditing = () => {
      const { anchorEl } = this.state;
  
      // Put back the focus after closing the inline popover.
      // $FlowFixMe
      if (anchorEl) anchorEl.focus();
  
      this.setState({
        editingObject: false,
        editingPreviousValue: null,
        anchorEl: null,
      });
    };
  
    toggleExpanded = () => {
      const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
      jsCodeEvent.setEventsSheetExpanded(!jsCodeEvent.isEventsSheetExpanded());
    };
  
    _getCodeEditorHeight = () => {
      const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
  
      // Always use the minimum height when collapsed.
      if (!jsCodeEvent.isEventsSheetExpanded()) {
        return MINIMUM_EDITOR_HEIGHT;
      }
  
      // Shrink the editor enough for the additional event elements to fit in the sheet space.
      const heightToFillSheet = this.props.eventsSheetHeight - EDITOR_PADDING;
      return Math.max(MINIMUM_EDITOR_HEIGHT, heightToFillSheet);
    };
  
    render() {
      const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
      const parameterObjects = jsCodeEvent.getParameterObjects();
  
      const textStyle = this.props.disabled ? styles.comment : undefined;
  
      const objects = (
        <span
          className={classNames({
            [selectableArea]: true,
          })}
          onClick={this.editObject}
          onKeyPress={event => {
            if (shouldActivate(event)) {
              this.editObject(event);
            }
          }}
          tabIndex={0}
          style={textStyle}
        >
          {parameterObjects ? (
            <Trans>, objects /*{parameterObjects}*/</Trans>
          ) : (
            <>
              {' '}
              <Trans>
                {'/* Click here to choose objects to pass to JavaScript */'}
              </Trans>
            </>
          )}
        </span>
      );
  
      const eventsFunctionContext = this.props.scope.eventsFunction ? (
        <span style={textStyle}>, eventsFunctionContext</span>
      ) : null;
  
      const functionStart = (
        <p style={styles.wrappingText}>
          <span style={textStyle}>
            {this.props.disabled ? '/*' : ''}
            {'(function(runtimeScene'}
          </span>
          {objects}
          {eventsFunctionContext}
          <span style={textStyle}>{') {'}</span>
        </p>
      );
      const functionEnd = (
        <p style={styles.wrappingText}>
          <span style={textStyle}>{'})(runtimeScene'}</span>
          {objects}
          {eventsFunctionContext}
          <span style={textStyle}>
            {');'}
            {this.props.disabled ? '*/' : ''}
          </span>
          <span style={styles.comment}>
            {' // '}
            <a
              href={getHelpLink('/events/js-code')}
              target="_blank"
              rel="noopener noreferrer"
              style={styles.commentLink}
            >
              Read the documentation and help
            </a>
          </span>
        </p>
      );
  
      const expandIcon = (
        <div style={styles.expandIcon}>
          {jsCodeEvent.isEventsSheetExpanded() ? (
            <ExpandLess fontSize="small" color="inherit" />
          ) : (
            <ExpandMore fontSize="small" color="inherit" />
          )}
        </div>
      );
  
      return (
        <Measure bounds>
          {({ measureRef, contentRect }) => (
            <div
              style={styles.container}
              className={classNames({
                [largeSelectableArea]: true,
                [largeSelectedArea]: this.props.selected,
              })}
              ref={measureRef}
            >
              {functionStart}
              <CodeEditor
                value={jsCodeEvent.getInlineCode()}
                onChange={this.onChange}
                width={contentRect.bounds.width - 5}
                height={this._getCodeEditorHeight()}
                onEditorMounted={() => this.props.onUpdate()}
              />
              {functionEnd}
              <Button onClick={this.toggleExpanded} fullWidth size="small">
                {expandIcon}
              </Button>
              <InlinePopover
                open={this.state.editingObject}
                anchorEl={this.state.anchorEl}
                onRequestClose={this.cancelObjectEditing}
                onApply={this.endObjectEditing}
              >
                <ObjectField
                  project={this.props.project}
                  scope={this.props.scope}
                  globalObjectsContainer={this.props.globalObjectsContainer}
                  objectsContainer={this.props.objectsContainer}
                  value={parameterObjects}
                  onChange={text => {
                    jsCodeEvent.setParameterObjects(text);
                    this.props.onUpdate();
                  }}
                  isInline
                  onRequestClose={this.cancelObjectEditing}
                  onApply={this.endObjectEditing}
                  ref={objectField => (this._objectField = objectField)}
                />
              </InlinePopover>
            </div>
          )}
        </Measure>
      );
    }
  }
  